在Ruby/Rails中,我该如何对URL中的特殊字符进行编码/转义?

19

在使用OpenURI的open(url)方法之前,我该如何对URL进行编码或转义?

我们正在使用OpenURI打开远程URL并返回XML:

getresult = open(url).read

问题在于URL包含一些用户输入的文本,其中包含空格和其他字符,包括"+", "&", "?",等等,因此我们需要安全地转义URL。我看到了很多使用Net::HTTP的例子,但没有发现任何关于OpenURI的。

我们还需要能够取消转义接收到的类似字符串,因此我们需要对应的函数。

4个回答

35

请勿使用 URI.escape,因为它已在1.9中被弃用。

Rails的Active Support添加了Hash#to_query

 {foo: 'asd asdf', bar: '"<#$dfs'}.to_query
 # => "bar=%22%3C%23%24dfs&foo=asd+asdf"

此外,正如您所看到的,它始终尝试以相同的方式对查询参数进行排序,这对于HTTP缓存是有益的。


在Rails 4.2中,我注意到这段代码运行如下: "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}" - Ed_
2
@Ed_,谢谢 - 我已经将无效的链接粘贴到了Object#to_query,而应该是Hash#to_query。 - Ernest

15

使用 Ruby 标准库来拯救:

require 'uri'
user_text = URI.escape(user_text)
url = "http://example.com/#{user_text}"
result = open(url).read

请查看URI::Escape模块的文档。该模块也有一个方法来执行相反的操作(unescape)。


也非常有帮助,谢谢。我不确定我会使用uri还是addressable。谢谢! - jpw
2
哦,刚看到URI.encode需要完整的URL。难怪会出问题。所以...不要使用它;) - Jacob
WTF?URI.encode 的规范绝对是不可能的;没有办法识别字符串中未转义的部分——这只是一个等待发生的安全漏洞。 - Eamon Nerbonne

8
你需要考虑的主要问题是,在组成完整的URL之前,必须分别转义键和值。
所有试图在获取完整URL后进行转义的方法都是错误的,因为它们无法确定任何“&”或“=”字符是否应该作为分隔符,还是作为值(或键的一部分)。
CGI库似乎做得很好,除了空格字符,它传统上被编码为“+”,现在应该编码为“%20”。但这很容易解决。
请参考以下内容:
require 'cgi'

def encode_component(s)
  # The space-encoding is a problem:
  CGI.escape(s).gsub('+','%20')
end

def url_with_params(path, args = {})
  return path if args.empty?
  path + "?" + args.map do |k,v|
    "#{encode_component(k.to_s)}=#{encode_component(v.to_s)}" 
  end.join("&")
end

def params_from_url(url)
  path,query = url.split('?',2)
  return [path,{}] unless query
  q = query.split('&').inject({}) do |memo,p|
    k,v = p.split('=',2)
    memo[CGI.unescape(k)] = CGI.unescape(v)
    memo
  end
  return [path, q]
end

u = url_with_params( "http://example.com",
                            "x[1]"  => "& ?=/",
                            "2+2=4" => "true" )

# "http://example.com?x%5B1%5D=%26%20%3F%3D%2F&2%2B2%3D4=true"

params_from_url(u)
# ["http://example.com", {"x[1]"=>"& ?=/", "2+2=4"=>"true"}]

2
不要使用CGI.escape,它违反了规范,将空格转换为+而不是%20。 - bluesmoon
我不明白。当我们谈论在URI中转义空格时,“+”是完全有效的,我相信。你为什么认为它不应该被使用? - Arsen7
1
Arsen7,+已经被弃用了。这是在URL编码标准化之前旧的CGI时代使用的符号。+仍然有效的唯一原因是为了向后兼容性。 - bluesmoon
1
你说得完全正确,但问题在于当时没有其他可靠的方法来正确转义URI组件。CGI::escape可以正确地执行所有操作,除了+,你可能只需对结果进行gsub。但是,如果你使用的是**ruby 1.9+**,那么似乎可以使用函数URI.encode_www_form_component代替。 - Arsen7

2
Ruby内置了URI库和Addressable宝石,特别是Addressable::URI
我更喜欢使用Addressable::URI。它非常功能齐全,在使用query_values=方法时会为您处理编码。
我看到过一些关于URI经历了一些成长烦恼的讨论,所以我倾向于不要在处理编码/转义方面进行操作,直到这些问题得到解决。

1
URI库已经过时,请参考https://dev59.com/eHE85IYBdhLWcg3wViA4。 - Marc-André Lafortune

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接