on master help sign in
Merge pull request #362 from yesodweb/pb-is-secure

Extend isSecure and remove caveats about reverse proxy

commit3a2a7da07a41360f92910846d0174cb8c0547a94 authorMichael Snoyman <michael@snoyman.com> dateWed 8 Apr 2015, 18:45 (12 years ago) parent9d56717f139be7
4 files changed+106−1
wai-extra/ChangeLog.md+5 wai-extra/Network/Wai/Request.hs+36 wai-extra/test/Network/Wai/RequestSpec.hs+63 wai-extra/wai-extra.cabal+2−1
wai-extra/ChangeLog.md+5
@@ -1,3 +1,8 @@
1+## 3.0.7
2+
3+* Add appearsSecure: check if a request appears to be using SSL even in the
4+ presence of reverse proxies [#362](https://github.com/yesodweb/wai/pull/362)
5+
16## 3.0.6.1
27
38* Test code: only include a Cookie header if there are cookies. Without this
wai-extra/Network/Wai/Request.hsnew file+36
@@ -0,0 +1,36 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+-- | Some helpers for interrogating a WAI 'Request'.
3+
4+module Network.Wai.Request
5+ ( appearsSecure
6+ ) where
7+
8+import Data.ByteString (ByteString)
9+import Network.HTTP.Types (HeaderName)
10+import Network.Wai (Request, isSecure, requestHeaders)
11+
12+import qualified Data.ByteString.Char8 as C
13+
14+-- | Does this request appear to have been made over an SSL connection?
15+--
16+-- This function first checks @'isSecure'@, but also checks for headers that may
17+-- indicate a secure connection even in the presence of reverse proxies.
18+--
19+-- Note: these headers can be easily spoofed, so decisions which require a true
20+-- SSL connection (i.e. sending sensitive information) should only use
21+-- @'isSecure'@. This is not always the case though: for example, deciding to
22+-- force a non-SSL request to SSL by redirect. One can safely choose not to
23+-- redirect when the request /appears/ secure, even if it's actually not.
24+--
25+-- Since 3.0.7
26+appearsSecure :: Request -> Bool
27+appearsSecure request = isSecure request || any (uncurry matchHeader)
28+ [ ("HTTPS" , (== "on"))
29+ , ("HTTP_X_FORWARDED_SSL" , (== "on"))
30+ , ("HTTP_X_FORWARDED_SCHEME", (== "https"))
31+ , ("HTTP_X_FORWARDED_PROTO" , ((== ["https"]) . take 1 . C.split ','))
32+ ]
33+
34+ where
35+ matchHeader :: HeaderName -> (ByteString -> Bool) -> Bool
36+ matchHeader h f = maybe False f $ lookup h $ requestHeaders request
wai-extra/test/Network/Wai/RequestSpec.hsnew file+63
@@ -0,0 +1,63 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+module Network.Wai.RequestSpec
3+ ( main
4+ , spec
5+ ) where
6+
7+import Test.Hspec
8+
9+import Data.ByteString (ByteString)
10+import Network.HTTP.Types (HeaderName)
11+import Network.Wai (Request(..), defaultRequest)
12+
13+import Network.Wai.Request
14+
15+main :: IO ()
16+main = hspec spec
17+
18+spec :: Spec
19+spec = describe "appearsSecure" $ do
20+ let insecureRequest = defaultRequest
21+ { isSecure = False
22+ , requestHeaders =
23+ [ ("HTTPS", "off")
24+ , ("HTTP_X_FORWARDED_SSL", "off")
25+ , ("HTTP_X_FORWARDED_SCHEME", "http")
26+ , ("HTTP_X_FORWARDED_PROTO", "http,xyz")
27+ ]
28+ }
29+
30+ it "returns False for an insecure request" $
31+ insecureRequest `shouldSatisfy` not . appearsSecure
32+
33+ it "checks if the Request is actually secure" $ do
34+ let req = insecureRequest { isSecure = True }
35+
36+ req `shouldSatisfy` appearsSecure
37+
38+ it "checks for HTTP: on" $ do
39+ let req = addHeader "HTTPS" "on" insecureRequest
40+
41+ req `shouldSatisfy` appearsSecure
42+
43+ it "checks for HTTP_X_FORWARDED_SSL: on" $ do
44+ let req = addHeader "HTTP_X_FORWARDED_SSL" "on" insecureRequest
45+
46+ req `shouldSatisfy` appearsSecure
47+
48+ it "checks for HTTP_X_FORWARDED_SCHEME: https" $ do
49+ let req = addHeader "HTTP_X_FORWARDED_SCHEME" "https" insecureRequest
50+
51+ req `shouldSatisfy` appearsSecure
52+
53+ it "checks for HTTP_X_FORWARDED_PROTO: https,..." $ do
54+ let req = addHeader "HTTP_X_FORWARDED_PROTO" "https,xyz" insecureRequest
55+
56+ req `shouldSatisfy` appearsSecure
57+
58+addHeader :: HeaderName -> ByteString -> Request -> Request
59+addHeader name value req = req
60+ { requestHeaders = (name, value) : otherHeaders }
61+
62+ where
63+ otherHeaders = filter ((/= name) . fst) $ requestHeaders req
wai-extra/wai-extra.cabal+2−1
@@ -1,5 +1,5 @@
11Name: wai-extra
2−Version: 3.0.6.1
2+Version: 3.0.7
33Synopsis: Provides some basic WAI handlers and middleware.
44description:
55 Provides basic WAI handler and middleware functionality:
@@ -132,6 +132,7 @@ Library
132132 Network.Wai.Middleware.HttpAuth
133133 Network.Wai.Middleware.StreamFile
134134 Network.Wai.Parse
135+ Network.Wai.Request
135136 Network.Wai.UrlMap
136137 Network.Wai.Test
137138 Network.Wai.EventSource