Permalink
Browse files

add functionality to block internet pages from accessing LAN hosts

Inspired by <https://code.google.com/p/chromium/issues/detail?id=378566>,
this adds a setting which defaults to on that will check each origin
host's DNS records and remember if any of them resolve to an RFC3330
local network IP.  If not, meaning the page is on the internet, then
block any page sub-requests that try to load from hosts that do
resolve to local networks.

This functionality is enough to block a silly attack like a webpage
on the internet having:

    <img src="http://192.168.100.1/reset.htm?reset_modem=Restart+Cable+Modem">

which does in fact restart my cable modem with this setting disabled
(and in Firefox).

Since iOS has its own internal DNS cache, these extra DNS lookups
do not actually hit the wire as verified with tcpdump (although it
would be nice to do this ourselves once and hand off an IP to the
URL loading system so we know for sure what it's talking to).
  • Loading branch information...
jcs committed Jan 12, 2015
1 parent fe800af commit d16359e4f2ce660ca0cf5171647cf54f94e80a33
@@ -29,6 +29,7 @@
01D7412C1A45F8EB007B7033 /* injected.js in Resources */ = {isa = PBXBuildFile; fileRef = 01D7412B1A45F8EB007B7033 /* injected.js */; };
01D7412F1A466AF0007B7033 /* NSString+JavascriptEscape.m in Sources */ = {isa = PBXBuildFile; fileRef = 01D7412E1A466AF0007B7033 /* NSString+JavascriptEscape.m */; };
01D741321A49EA14007B7033 /* HTTPSEverywhereRuleController.m in Sources */ = {isa = PBXBuildFile; fileRef = 01D741311A49EA14007B7033 /* HTTPSEverywhereRuleController.m */; };
01EFA7FF1A63851400688398 /* LocalNetworkChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 01EFA7FE1A63851400688398 /* LocalNetworkChecker.m */; };
01F7CB491A5253DD00F42B73 /* HSTSCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 01F7CB481A5253DD00F42B73 /* HSTSCache.m */; };
01F7CB4B1A526B9C00F42B73 /* HSTSCache_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01F7CB4A1A526B9C00F42B73 /* HSTSCache_Tests.m */; };
01F7CB4E1A52FC4E00F42B73 /* NSString+IPAddress.m in Sources */ = {isa = PBXBuildFile; fileRef = 01F7CB4D1A52FC4E00F42B73 /* NSString+IPAddress.m */; };
@@ -97,6 +98,8 @@
01D7412E1A466AF0007B7033 /* NSString+JavascriptEscape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+JavascriptEscape.m"; sourceTree = "<group>"; };
01D741301A49EA14007B7033 /* HTTPSEverywhereRuleController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPSEverywhereRuleController.h; sourceTree = "<group>"; };
01D741311A49EA14007B7033 /* HTTPSEverywhereRuleController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPSEverywhereRuleController.m; sourceTree = "<group>"; };
01EFA7FD1A63851400688398 /* LocalNetworkChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalNetworkChecker.h; sourceTree = "<group>"; };
01EFA7FE1A63851400688398 /* LocalNetworkChecker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalNetworkChecker.m; sourceTree = "<group>"; };
01F7CB471A5253DD00F42B73 /* HSTSCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSTSCache.h; sourceTree = "<group>"; };
01F7CB481A5253DD00F42B73 /* HSTSCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSTSCache.m; sourceTree = "<group>"; };
01F7CB4A1A526B9C00F42B73 /* HSTSCache_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSTSCache_Tests.m; sourceTree = "<group>"; };
@@ -200,6 +203,8 @@
children = (
018333CF1A351B3B00670CD1 /* Endless-Prefix.pch */,
01801E961A32CA2A002B4718 /* Info.plist */,
01EFA7FD1A63851400688398 /* LocalNetworkChecker.h */,
01EFA7FE1A63851400688398 /* LocalNetworkChecker.m */,
01801E971A32CA2A002B4718 /* main.m */,
01F7CB4C1A52FC4E00F42B73 /* NSString+IPAddress.h */,
01F7CB4D1A52FC4E00F42B73 /* NSString+IPAddress.m */,
@@ -466,6 +471,7 @@
01F7CB4E1A52FC4E00F42B73 /* NSString+IPAddress.m in Sources */,
0135F47F1A3E548F005A8F16 /* WebViewTab.m in Sources */,
010EEA661A43A536001E8B65 /* CookieController.m in Sources */,
01EFA7FF1A63851400688398 /* LocalNetworkChecker.m in Sources */,
010EEA691A43C8CF001E8B65 /* CookieJar.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -64,6 +64,24 @@
<key>IASKTextAlignment</key>
<string>IASKUITextAlignmentRight</string>
</dict>
<dict>
<key>Title</key>
<string></string>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>FooterText</key>
<string>When enabled, URLs loaded from the internet will be blocked from loading page elements or making requests to LAN hosts (192.168.0.0/16, 172.16.0.0/12, etc.)</string>
</dict>
<dict>
<key>Key</key>
<string>block_into_local_nets</string>
<key>Title</key>
<string>Block external LAN requests</string>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>DefaultValue</key>
<true/>
</dict>
<dict>
<key>Title</key>
<string></string>
Binary file not shown.
@@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
@interface LocalNetworkChecker : NSObject
+ (void)clearCache;
+ (BOOL)isHostOnLocalNet:(NSString *)host;
@end
@@ -0,0 +1,123 @@
#import "LocalNetworkChecker.h"
#import <netinet/in.h>
#import <netdb.h>
#import <ifaddrs.h>
#import <arpa/inet.h>
@implementation LocalNetworkChecker
static NSArray *localNets;
static NSMutableDictionary *dnsCache;
+ (void)clearCache
{
if (dnsCache) {
[dnsCache removeAllObjects];
}
else {
dnsCache = [[NSMutableDictionary alloc] initWithCapacity:20];
}
}
/* iOS has its internal DNS cache, so this does not have to send out another request on the wire */
+ (NSArray *)addressesForHostname:(NSString *)host {
if (!dnsCache) {
[[self class] clearCache];
}
id cached = [dnsCache objectForKey:[host lowercaseString]];
if (cached != nil) {
NSDictionary *dcache = (NSDictionary *)cached;
NSDate *t = [dcache objectForKey:@"time"];
if ([[NSDate date] timeIntervalSinceDate:t] < 30) {
return [dcache objectForKey:@"addresses"];
}
}
CFHostRef hostRef = CFHostCreateWithName(kCFAllocatorDefault, (__bridge CFStringRef)host);
if (!CFHostStartInfoResolution(hostRef, kCFHostAddresses, nil))
return nil;
CFArrayRef addressesRef = CFHostGetAddressing(hostRef, nil);
if (addressesRef == nil)
return nil;
char ipAddress[INET6_ADDRSTRLEN];
NSMutableArray *addresses = [NSMutableArray array];
CFIndex numAddresses = CFArrayGetCount(addressesRef);
for (CFIndex currentIndex = 0; currentIndex < numAddresses; currentIndex++) {
struct sockaddr *address = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addressesRef, currentIndex));
if (address == nil)
return nil;
getnameinfo(address, address->sa_len, ipAddress, INET6_ADDRSTRLEN, nil, 0, NI_NUMERICHOST);
if (ipAddress == nil)
return nil;
[addresses addObject:[NSString stringWithCString:ipAddress encoding:NSASCIIStringEncoding]];
}
[dnsCache setValue:@{ @"addresses" : addresses, @"time" : [NSDate date] } forKey:[host lowercaseString]];
return addresses;
}
+ (BOOL)isHostOnLocalNet:(NSString *)host
{
if (!localNets) {
NSMutableArray *tLocalNets = [[NSMutableArray alloc] initWithCapacity:11];
[@{
/* rfc3330 */
@"0.0.0.0" : @8,
@"10.0.0.0" : @8,
@"127.0.0.0" : @8,
@"169.254.0.0" : @16,
@"172.16.0.0" : @12,
@"192.0.2.0" : @24,
@"192.88.99.0" : @24,
@"192.168.0.0" : @16,
@"198.18.0.0" : @15,
@"224.0.0.0" : @4,
@"240.0.0.0" : @4,
} enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
struct in_addr addr;
if (inet_aton([key UTF8String], &addr) != 0) {
uint32_t ip = ntohl(addr.s_addr);
int cidr = [(NSNumber *)value intValue];
uint32_t last = ip + (uint32_t)pow(2, (32 - cidr)) - 1;
[tLocalNets addObject:@[ [NSNumber numberWithInt:ip], [NSNumber numberWithInt:last] ]];
}
}];
localNets = [NSArray arrayWithArray:tLocalNets];
}
NSArray *ips = [[self class] addressesForHostname:host];
if (ips == nil)
return NO;
for (NSString *ip in ips) {
struct in_addr addr;
if (inet_aton([ip UTF8String], &addr) == 0) {
continue;
}
uint32_t uip = ntohl(addr.s_addr);
for (NSArray *net in localNets) {
if (uip >= [((NSNumber *)net[0]) intValue] && uip <= [((NSNumber *)net[1]) intValue]) {
#ifdef TRACE
NSLog(@"ip of host %@ (%@) is between %@", host, ip, net);
#endif
return YES;
}
}
}
return NO;
}
@end
View
@@ -10,6 +10,7 @@
@property (strong) NSString *evOrgName;
@property (strong) NSURLConnection *connection;
+ (void)setBlockIntoLocalNets:(BOOL)val;
+ (void)setSendDNT:(BOOL)val;
@end
View
@@ -1,6 +1,7 @@
#import "AppDelegate.h"
#import "HSTSCache.h"
#import "HTTPSEverywhere.h"
#import "LocalNetworkChecker.h"
#import "URLBlocker.h"
#import "URLInterceptor.h"
#import "WebViewTab.h"
@@ -9,6 +10,7 @@ @implementation URLInterceptor
static AppDelegate *appDelegate;
static BOOL sendDNT = true;
static BOOL blockIntoLocalNets = true;
WebViewTab *wvt;
NSString *userAgent;
@@ -35,6 +37,11 @@ + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
return request;
}
+ (void)setBlockIntoLocalNets:(BOOL)val
{
blockIntoLocalNets = val;
}
+ (void)setSendDNT:(BOOL)val
{
sendDNT = val;
@@ -96,7 +103,10 @@ - (void)startLoading
self.isOrigin = YES;
}
if (!self.isOrigin) {
if (self.isOrigin) {
[LocalNetworkChecker clearCache];
}
else {
if ([URLBlocker shouldBlockURL:[newRequest URL] fromMainDocumentURL:[newRequest mainDocumentURL]]) {
cancelLoading();
return;
@@ -123,6 +133,14 @@ - (void)startLoading
cancelLoading();
return;
}
if (blockIntoLocalNets) {
if (![LocalNetworkChecker isHostOnLocalNet:[[newRequest mainDocumentURL] host]] && [LocalNetworkChecker isHostOnLocalNet:[[newRequest URL] host]]) {
NSLog(@"[Tab %@] blocking request from origin %@ to local net host %@", wvt.tabNumber, [newRequest mainDocumentURL], [newRequest URL]);
cancelLoading();
return;
}
}
}
/* we're handling cookies ourself */
@@ -741,6 +741,7 @@ - (void)settingsViewControllerDidEnd:(IASKAppSettingsViewController *)sender
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[URLInterceptor setSendDNT:[userDefaults boolForKey:@"send_dnt"]];
[URLInterceptor setBlockIntoLocalNets:[userDefaults boolForKey:@"block_into_local_nets"]];
[[appDelegate cookieJar] setOldDataSweepTimeout:[NSNumber numberWithInteger:[userDefaults integerForKey:@"old_data_sweep_mins"]]];
}
View
@@ -34,8 +34,8 @@ course) with a design goal of increased security and privacy.
- Integrated full [HTTPS Everywhere](https://www.eff.org/HTTPS-EVERYWHERE)
ruleset (currently over 11,000 rules) to do on-the-fly URL rewriting to force
requests over SSL where supported, including setting the secure
bit on received cookies and auto-detection of redirection loops
requests over SSL where supported, including setting the secure bit on
received cookies and auto-detection of redirection loops
- HTTP Strict Transport Security (RFC6797) implementation (in addition to
WebKit's mystery built-in one) with Chromium's large preload list
@@ -48,6 +48,10 @@ course) with a design goal of increased security and privacy.
- Blocks mixed-content requests (http elements on an https page), shows broken
padlock
- Blocks pages loaded from non-local networks (i.e., the internet) from trying
to load sub-requests (e.g., images, iframes, ajax) from hosts that are on
local RFC3330 networks such as routers and other insecure devices
- Shows organization name in URL bar for sites with EV SSL certs
- Optional sending of Do-Not-Track header on all requests

0 comments on commit d16359e

Please sign in to comment.