为何部分URL浏览器可正常访问,使用Lua的http(s).request却请求失败
问题原因分析
你遇到的问题是由以下几个常见原因导致的,均和ssl.https的默认配置限制有关:
- TLS SNI扩展缺失:ics.fixtur.es使用多域名共享IP的HTTPS部署方案,要求客户端在TLS握手阶段发送SNI(服务器名称指示)扩展才能正确获取站点证书、完成TLS握手。而
ssl.https默认配置下不会主动发送SNI,Google日历的HTTPS服务对旧客户端兼容性更强,不需要SNI也能正常响应,因此只有B链接可以正常返回结果。 - User-Agent校验限制:部分服务端会拦截默认的
LuaSocket类爬虫UA、或者UA为空的请求,直接拒绝返回内容。 - TLS版本限制:ics.fixtur.es可能已经禁用了安全性较低的TLS 1.0/1.1版本,如果你使用的Lua ssl库默认协商的TLS版本过低,也会导致TLS握手失败。
你提到的redirect配置用法如下:该参数接收布尔值,设为true时请求会自动跟随3xx重定向响应(默认即为开启状态),设为false时会直接返回重定向的状态码和响应头,不会自动跳转。你已经排查过目标链接无重定向,因此该参数不是导致当前问题的原因。
解决方法
你可以通过自定义请求参数、补充SNI配置、添加合法UA头来解决问题,修改后的测试代码如下:
local socket = require 'socket' local http = require "socket.http" local ssl = require 'ssl' local https = require 'ssl.https' local ltn12 = require 'ltn12' local calendarUria = "https://ics.fixtur.es/v2/ipswich-town.ics" local calendarUrib = "https://calendar.google.com/calendar/ical/en-gb.uk%23holiday%40group.v.calendar.google.com/public/basic.ics" -- 自定义支持SNI的create函数 local function create() local sock = socket.tcp() return setmetatable({ connect = function(_, host, port) local ok, err = sock:connect(host, port) if not ok then return nil, err end -- TLS握手时添加SNI扩展 sock = ssl.wrap(sock, { mode = 'client', protocol = 'any', -- 自动协商最高支持的TLS版本 options = {'all', 'no_sslv2', 'no_sslv3', 'no_tlsv1', 'no_tlsv1_1'}, -- 禁用不安全的旧协议 verify = 'none', -- 如果需要校验证书可以改为'peer',同时配置cafile }) sock:sni(host) return sock:dohandshake() end }, { __index = function(t, n) return function(_, ...) return sock[n](sock, ...) end end }) end -- 统一请求函数 local function request(url) local res = {} local _, code, headers = https.request{ url = url, sink = ltn12.sink.table(res), create = create, headers = { ['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36', ['Accept'] = 'text/calendar,*/*' }, redirect = true -- 自动跟随重定向,默认开启可省略 } return table.concat(res), code, headers end print("Fixtur.es Football (URL A) returns the following?") local responseBodya, responseCodea, responseHeadersa = request(calendarUria) print(responseCodea, #responseBodya) print("Google UK Holidays (URL B) returns the following?") local responseBodyb, responseCodeb, responseHeadersb = request(calendarUrib) print(responseCodeb, #responseBodyb)
如果需要开启证书校验避免中间人攻击,只需要在ssl.wrap的配置中添加cafile = "你的根证书路径",并将verify改为'peer'即可。
内容的提问来源于stack exchange,提问作者nodecentral
相关产品推荐
相关产品推荐

