unxf 2.0.0 → 2.0.0.2.g32d0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/unxf.rb +18 -6
- data/test/test_unxf.rb +38 -0
- metadata +16 -9
data/lib/unxf.rb
CHANGED
@@ -10,15 +10,20 @@ class UnXF
|
|
10
10
|
HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR"
|
11
11
|
HTTP_X_FORWARDED_PROTO = "HTTP_X_FORWARDED_PROTO"
|
12
12
|
RACK_URL_SCHEME = "rack.url_scheme".freeze
|
13
|
+
UNXF_FOR = "unxf.for".freeze
|
14
|
+
UNXF_PROTO = "unxf.proto".freeze
|
13
15
|
HTTPS = "https"
|
14
16
|
# :startdoc:
|
15
17
|
|
16
18
|
# local LAN addresses described in RFC 1918
|
17
19
|
RFC_1918 = %w(10.0.0.0/8 172.16.0.0/12 192.168.0.0/16)
|
18
20
|
|
19
|
-
# localhost addresses (127.0.0.0/8)
|
21
|
+
# IPv4 localhost addresses (127.0.0.0/8)
|
20
22
|
LOCALHOST = %w(127.0.0.0/8)
|
21
23
|
|
24
|
+
# IPv6 localhost address (::1/128)
|
25
|
+
LOCALHOST6 = %w(::1/128)
|
26
|
+
|
22
27
|
# In your Rack config.ru:
|
23
28
|
#
|
24
29
|
# use UnXF
|
@@ -32,12 +37,15 @@ class UnXF
|
|
32
37
|
#
|
33
38
|
# use UnXF, [ :RFC_1918, :LOCALHOST, "0.6.6.6" ]
|
34
39
|
#
|
35
|
-
def initialize(app, trusted = [:RFC_1918, :LOCALHOST])
|
40
|
+
def initialize(app, trusted = [:RFC_1918, :LOCALHOST, :LOCALHOST6])
|
36
41
|
@app = app
|
37
42
|
@trusted = Patricia.new
|
43
|
+
@trusted6 = Patricia.new(:AF_INET6)
|
38
44
|
Array(trusted).each do |mask|
|
39
45
|
mask = UnXF.const_get(mask) if Symbol === mask
|
40
|
-
Array(mask).each
|
46
|
+
Array(mask).each do |prefix|
|
47
|
+
(/:/ =~ prefix ? @trusted6 : @trusted).add(prefix, true)
|
48
|
+
end
|
41
49
|
end
|
42
50
|
end
|
43
51
|
|
@@ -51,10 +59,12 @@ class UnXF
|
|
51
59
|
# into the middleware stack (to avoid increasing stack depth and GC time)
|
52
60
|
def unxf!(env)
|
53
61
|
if xff_str = env.delete(HTTP_X_FORWARDED_FOR)
|
62
|
+
env[UNXF_FOR] = xff_str
|
54
63
|
xff = xff_str.split(/\s*,\s*/)
|
55
64
|
addr = env[REMOTE_ADDR]
|
56
65
|
begin
|
57
|
-
while @trusted.include?(addr) &&
|
66
|
+
while (/:/ =~ addr ? @trusted6 : @trusted).include?(addr) &&
|
67
|
+
tmp = xff.pop
|
58
68
|
addr = tmp
|
59
69
|
end
|
60
70
|
rescue ArgumentError
|
@@ -65,8 +75,10 @@ class UnXF
|
|
65
75
|
# proxy in the chain, so we don't support that
|
66
76
|
if xff.empty?
|
67
77
|
env[REMOTE_ADDR] = addr
|
68
|
-
env.delete(HTTP_X_FORWARDED_PROTO)
|
69
|
-
|
78
|
+
if xfp = env.delete(HTTP_X_FORWARDED_PROTO)
|
79
|
+
env[UNXF_PROTO] = xfp
|
80
|
+
/\Ahttps\b/ =~ xfp and env[RACK_URL_SCHEME] = HTTPS
|
81
|
+
end
|
70
82
|
else
|
71
83
|
return on_untrusted_addr(env, xff_str)
|
72
84
|
end
|
data/test/test_unxf.rb
CHANGED
@@ -25,6 +25,36 @@ class TestUnXF < Test::Unit::TestCase
|
|
25
25
|
assert_equal 200, r.status.to_i
|
26
26
|
assert_equal "0.6.6.6", @env["REMOTE_ADDR"]
|
27
27
|
assert ! @env.key?("HTTP_X_FORWARDED_FOR")
|
28
|
+
assert_equal "0.6.6.6", @env["unxf.for"]
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_single_proxy_https
|
32
|
+
req = Rack::MockRequest.new(UnXF.new(@app))
|
33
|
+
env = {
|
34
|
+
"HTTP_X_FORWARDED_FOR" => "0.6.6.6",
|
35
|
+
"HTTP_X_FORWARDED_PROTO" => "https",
|
36
|
+
"REMOTE_ADDR" => "127.0.0.1",
|
37
|
+
}
|
38
|
+
r = req.get("http://example.com/", @req.merge(env))
|
39
|
+
assert_equal 200, r.status.to_i
|
40
|
+
assert_equal "0.6.6.6", @env["REMOTE_ADDR"]
|
41
|
+
assert ! @env.key?("HTTP_X_FORWARDED_FOR")
|
42
|
+
assert_equal "0.6.6.6", @env["unxf.for"]
|
43
|
+
assert_equal "https", @env["unxf.proto"]
|
44
|
+
assert_equal "https", @env["rack.url_scheme"]
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_ipv6_localhost
|
48
|
+
req = Rack::MockRequest.new(UnXF.new(@app))
|
49
|
+
env = {
|
50
|
+
"HTTP_X_FORWARDED_FOR" => "2600:3c01::f03c:91ff:fe96:f5d6",
|
51
|
+
"REMOTE_ADDR" => "::1",
|
52
|
+
}
|
53
|
+
r = req.get("http://example.com/", @req.merge(env))
|
54
|
+
assert_equal 200, r.status.to_i
|
55
|
+
assert_equal "2600:3c01::f03c:91ff:fe96:f5d6", @env["REMOTE_ADDR"]
|
56
|
+
assert ! @env.key?("HTTP_X_FORWARDED_FOR")
|
57
|
+
assert_equal "2600:3c01::f03c:91ff:fe96:f5d6", @env["unxf.for"]
|
28
58
|
end
|
29
59
|
|
30
60
|
def test_multiple_proxies
|
@@ -37,6 +67,7 @@ class TestUnXF < Test::Unit::TestCase
|
|
37
67
|
assert_equal "0.6.6.6", @env["REMOTE_ADDR"]
|
38
68
|
assert_equal 200, r.status.to_i
|
39
69
|
assert ! @env.key?("HTTP_X_FORWARDED_FOR")
|
70
|
+
assert_equal "0.6.6.6,192.168.1.1", @env["unxf.for"]
|
40
71
|
end
|
41
72
|
|
42
73
|
def test_spoofed
|
@@ -48,6 +79,7 @@ class TestUnXF < Test::Unit::TestCase
|
|
48
79
|
r = req.get("http://example.com/", @req.merge(env))
|
49
80
|
assert_equal "227.0.0.1", @env["REMOTE_ADDR"]
|
50
81
|
assert_equal r.status.to_i, 200
|
82
|
+
assert_equal "0.6.6.6", @env["unxf.for"]
|
51
83
|
end
|
52
84
|
|
53
85
|
def test_trusted_chain
|
@@ -60,6 +92,7 @@ class TestUnXF < Test::Unit::TestCase
|
|
60
92
|
assert_equal 200, r.status.to_i
|
61
93
|
assert_equal "0.6.6.6", @env["REMOTE_ADDR"]
|
62
94
|
assert ! @env.key?("HTTP_X_FORWARDED_FOR")
|
95
|
+
assert_equal "0.6.6.6,192.168.0.1", @env["unxf.for"]
|
63
96
|
end
|
64
97
|
|
65
98
|
def test_spoofed_in_chain
|
@@ -71,6 +104,7 @@ class TestUnXF < Test::Unit::TestCase
|
|
71
104
|
r = req.get("http://example.com/", @req.merge(env))
|
72
105
|
assert_equal "127.0.0.1", @env["REMOTE_ADDR"]
|
73
106
|
assert_equal r.status.to_i, 200
|
107
|
+
assert_equal "0.6.6.6,8.8.8.8", @env["unxf.for"]
|
74
108
|
end
|
75
109
|
|
76
110
|
def test_spoofed_null_safe
|
@@ -82,6 +116,7 @@ class TestUnXF < Test::Unit::TestCase
|
|
82
116
|
r = req.get("http://example.com/", @req.merge(env))
|
83
117
|
assert_equal "127.0.0.1", @env["REMOTE_ADDR"]
|
84
118
|
assert_equal r.status.to_i, 200
|
119
|
+
assert_equal "\0.6.6.6,8.8.8.8", @env["unxf.for"]
|
85
120
|
end
|
86
121
|
|
87
122
|
def test_more_trust
|
@@ -94,6 +129,7 @@ class TestUnXF < Test::Unit::TestCase
|
|
94
129
|
assert_equal r.status.to_i, 200
|
95
130
|
assert_equal "1.6.6.6", @env["REMOTE_ADDR"]
|
96
131
|
assert ! @env.key?("HTTP_X_FORWARDED_FOR")
|
132
|
+
assert_equal "1.6.6.6,0.6.6.6", @env["unxf.for"]
|
97
133
|
end
|
98
134
|
|
99
135
|
def test_one_trusted
|
@@ -106,5 +142,7 @@ class TestUnXF < Test::Unit::TestCase
|
|
106
142
|
assert_equal r.status.to_i, 200
|
107
143
|
assert_equal "1.6.6.6", @env["REMOTE_ADDR"]
|
108
144
|
assert ! @env.key?("HTTP_X_FORWARDED_FOR")
|
145
|
+
assert_equal "1.6.6.6", @env["unxf.for"]
|
146
|
+
assert_nil @env["unxf.proto"]
|
109
147
|
end
|
110
148
|
end
|
metadata
CHANGED
@@ -1,13 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unxf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 2091
|
5
|
+
prerelease: 8
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 0
|
9
9
|
- 0
|
10
|
-
|
10
|
+
- 2
|
11
|
+
- g
|
12
|
+
- 32
|
13
|
+
- d
|
14
|
+
- 0
|
15
|
+
version: 2.0.0.2.g32d0
|
11
16
|
platform: ruby
|
12
17
|
authors:
|
13
18
|
- UnXF hackers
|
@@ -15,7 +20,7 @@ autorequire:
|
|
15
20
|
bindir: bin
|
16
21
|
cert_chain: []
|
17
22
|
|
18
|
-
date: 2011-
|
23
|
+
date: 2011-08-09 00:00:00 Z
|
19
24
|
dependencies:
|
20
25
|
- !ruby/object:Gem::Dependency
|
21
26
|
name: rack
|
@@ -119,16 +124,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
119
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
125
|
none: false
|
121
126
|
requirements:
|
122
|
-
- - "
|
127
|
+
- - ">"
|
123
128
|
- !ruby/object:Gem::Version
|
124
|
-
hash:
|
129
|
+
hash: 25
|
125
130
|
segments:
|
126
|
-
-
|
127
|
-
|
131
|
+
- 1
|
132
|
+
- 3
|
133
|
+
- 1
|
134
|
+
version: 1.3.1
|
128
135
|
requirements: []
|
129
136
|
|
130
137
|
rubyforge_project: rainbows
|
131
|
-
rubygems_version: 1.8.
|
138
|
+
rubygems_version: 1.8.5
|
132
139
|
signing_key:
|
133
140
|
specification_version: 3
|
134
141
|
summary: Un-X-Forward* the Rack environment
|