30
[ ] When Worlds Collide: Dave Hauver Software Developer HTTPS, Insecure Map Services, and You

When Worlds Collide: HTTPS, Insecure Map Services, and You

Embed Size (px)

Citation preview

[ ]When Worlds Collide:

Dave Hauver

Software Developer

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

What We Want

What We Get

Really, It’s Not Just Us

Drat.

Easy Fixes

• Change map service to use SSL

• Use an https proxy for http map services

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

Request

Solution: Access Control Headers

Response

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.

Questions?

????

This is your time. Do with it as you please.