When Worlds Collide: HTTPS, Insecure Map Services, and You
-
Upload
davehauverns -
Category
Software
-
view
101 -
download
3
Transcript of When Worlds Collide: HTTPS, Insecure Map Services, and You
End Goal
• Online map viewer using Esri Javascript API
• Map viewer is secured, and only available for certain users
• Map viewer must include a map service that is only available over http
The Main Problem
• Due to logins, site needs to support SSL
• Doing so causes a mixed content warning on the map viewer page
But What If…
Users need to be able to add arbitrary map services of their choice - including those that can't be proxied because they are behind the user's firewall
Our Solution
Map Viewer Wrapper(http only)
Security Check Web Service
JSON Response:{ "loggedIn": true, "allowed": true}
Map Viewer Main Body
https
Load over https and show
User Status?
Not Authorized Main Body Login Page
Load over https and showRedirect
Not AllowedAllowed
Not Logged In
General Approach
• Make the map viewer page available over http
• All other site content requires https
• The map viewer page is split into pieces–A thin wrapper is served over http– The wrapper uses a secure web service to
check authorization– The wrapper loads the main body using https
Is It Really That Easy?
A few things to consider…• Enforce http/https for individual pages
• Session hijacking
• Knowing user identity on anonymous page
• CORS– Access Control Headers– Cookie/Session Management
• Login process when map viewer page is bookmarked
No
Sample Project
• Java web application, run on Tomcat
• Apache web server supports http/s
• Apache proxies Tomcat using AJP– allows Tomcat to know original protocol
• tomcat-users.xml for user repository
• Git repository available at: https://bitbucket.org/natureserve/secure-map-viewer
Concern: Enforcing http/https for pages
Map viewer – requires http–https not allowed
Everything else – requires https–http not allowed.
Solution: Apache Rewrite Rules
<VirtualHost *:80> ## Redirect all traffic except map viewer to https RewriteEngine On RewriteCond %{REQUEST_URI} !(^(/secure-map-viewer/insecure/).*$) RewriteRule ^(.*)$ https://%{SERVER_NAME}$1 [R,NE,L]</VirtualHost>
<VirtualHost *:443> ## Redirect requests for the map viewer page back to http RewriteEngine On RewriteCond %{REQUEST_URI} (^(/secure-map-viewer/insecure/).*$) RewriteRule ^(.*)$ http://%{SERVER_NAME}$1 [R,NE,L]
Concern: Session Hijacking
• Sessions are maintained with cookie.
• Sending cookie over http is a security risk
Solution: Secure the Session Cookie
– Secure = the browser will only include the cookie if using https– HttpOnly = the cookie cannot be accessed using javascript
Tomcat does this automatically when sessions are created by https requests
Solution: Exclude http requests from session
• Apache configuration rule
• JSP page directive on (http) map viewer wrapper page
– This isn't strictly required, but prevents tomcat from creating and managing unnecessary sessions.
<VirtualHost *:80> Header unset Set-Cookie </VirtualHost>
<%@ page session="false" %>
Concern: Knowing User Identity
Map viewer wrapper is loaded over http
• Cannot access session information
• How to verify user authorization?
• How to tailor content to specific user?
Solution: Ajax to the rescue!
var baseUrl = "https://localdave.natureserve.org/secure-map-viewer";$(document).ready(function() { $.get(baseUrl + "/mapSecurityCheck.json", function(data) { if (data.loggedIn == false) { if (data.allowed) { $.get(baseUrl + "/restricted/map-body.jsp", function(data) { $("#mainBody").html(data); dojo.addOnLoad(initMapViewer); } else { $.get(baseUrl + "/restricted/not-authorized-body.jsp", function(data) { $("#mainBody").html(data); }); } } else { window.location = baseUrl + "/restricted/map-login.jsp"; } });});
– map-body.jsp is loaded using https. The request has access to the session, and can tailor content based on the user.
Concern: CORS
CORS = Cross-origin resource sharing
https://www.foo.com != http://www.foo.com
Web browser security 101: Pages loaded from domain A cannot communicate with domain B.– See Session-Highjacking 101
Unless... domain B says it's okay.
Solution: Access Control Headers
public class CORSFilter implements javax.servlet.Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest)request; HttpServletResponse resp = (HttpServletResponse)response;
if (req.getHeader("Origin") != null) { resp.setHeader("Access-Control-Allow-Origin", "http://" + req.getServerName()); resp.setHeader("Access-Control-Allow-Headers", req.getHeader("Access-Control-Request-Headers")); resp.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); resp.setHeader("Access-Control-Allow-Credentials", "true"); } if (!("OPTIONS".equals(req.getMethod()))) { chain.doFilter(request, response); } }
–Also possible to do through Apache rules
Concern: jQuery, cookies, and CORS
• We want https requests made by map viewer wrapper page to be part of the user’s session
• Session cookie must be sent
• By default, jQuery doesn’t include cookies for CORS requests.
Solution: withCredentials=true
var serverName = 'localdave.natureserve.org';
$.ajaxPrefilter(function( options, originalOptions, jqXHR ) { if (jQuery.support.cors) { // Turn relative URLs into fully qualified URLs that use https var url = options.url; if ((url.lastIndexOf('http://', 0) !== 0) && (url.lastIndexOf('https://', 0) !== 0)) { url = 'https://' + serverName + thisUrl; options.url = thisUrl; } // Ensure we send credentials for https requests back to this server. if (isUrlForServer(options.url)) { if(!(options.xhrFields)) { options.xhrFields = {}; } options.xhrFields.withCredentials = true; } }});
function isUrlForServer(url) { return (url.indexOf('http://' + serverName) == 0 || url.indexOf('https://' + serverName) == 0);}
Concern: Login Process When Map Page is
BookmarkedLogin page is only shown in response to attempts to access a restricted resource (For Java web applications)
Need to:
• Request a restricted resource to force login
• Redirect back to insecure map page afterward
Solution: Secured Redirect Page
map-login.jsp contents:
map.jsp – http wrapper, no restrictions
map-login.jsp: restricted through web.xml<security-constraint> <web-resource-collection> <web-resource-name>Map Viewer Login Page</web-resource-name> <url-pattern>/map-login.jsp</url-pattern> </web-resource-collection> <auth-constraint> <role-name>map</role-name> </auth-constraint></security-constraint>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><c:redirect url="/map.jsp"/>
Solution: Complete Login Process
URL Requested Response
http://www.foo.com/map.jsp Calls https web service and detects user is not logged in. Redirects to https://www.foo.com/map-login.jsp
https://www.foo.com/map-login.jsp Container detects attempt to access secure resource by unauthenticated user. Redirects to https://www.foo.com/login.jsp
https://www.foo.com/login.jsp User enters credentials, posts to https://www.foo.com/j_security_check
https://www.foo.com/j_security_check Login succeeds, redirects to page that triggered the login, https://www.foo.com/map-login.jsp
https://www.foo.com/map-login.jsp Page redirects to https://www.foo.com/map.jsp
https://www.foo.com/map.jsp Apache redirects to http://www.foo.com/map.jsp
http://www.foo.com/map.jsp Calls https web service and detects user is logged in. Loads https://www.foo.com/map-body.jsp and initializes map.