Skip to content

Commit 40e46d6

Browse files
Mossakaclaude
andauthored
fix(security): block direct IP connections bypassing filters (#1160)
Add Squid ACLs to deny CONNECT requests targeting raw IPv4 and IPv6 addresses. Without this, an attacker could bypass domain-based filtering by connecting directly to an IP address using HTTPS. Fixes #137 Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3656965 commit 40e46d6

File tree

2 files changed

+68
-1
lines changed

2 files changed

+68
-1
lines changed

src/squid-config.test.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ describe('generateSquidConfig', () => {
670670
};
671671
const result = generateSquidConfig(config);
672672
expect(result).toContain('acl allowed_domains dstdomain');
673-
expect(result).not.toContain('dstdom_regex');
673+
expect(result).not.toContain('acl allowed_domains_regex dstdom_regex');
674674
expect(result).toContain('http_access deny !allowed_domains');
675675
expect(result).not.toContain('allowed_domains_regex');
676676
});
@@ -1198,6 +1198,66 @@ describe('generateSquidConfig', () => {
11981198
});
11991199
});
12001200

1201+
describe('Direct IP bypass protection', () => {
1202+
const defaultPort = 3128;
1203+
1204+
it('should include IPv4 deny ACL in generated config', () => {
1205+
const config: SquidConfig = {
1206+
domains: ['github.com'],
1207+
port: defaultPort,
1208+
};
1209+
const result = generateSquidConfig(config);
1210+
expect(result).toContain('acl dst_ipv4 dstdom_regex');
1211+
expect(result).toContain('http_access deny dst_ipv4');
1212+
});
1213+
1214+
it('should include IPv6 deny ACL in generated config', () => {
1215+
const config: SquidConfig = {
1216+
domains: ['github.com'],
1217+
port: defaultPort,
1218+
};
1219+
const result = generateSquidConfig(config);
1220+
expect(result).toContain('acl dst_ipv6 dstdom_regex');
1221+
expect(result).toContain('http_access deny dst_ipv6');
1222+
});
1223+
1224+
it('should place IP deny rules before domain allow/deny rules', () => {
1225+
const config: SquidConfig = {
1226+
domains: ['github.com'],
1227+
port: defaultPort,
1228+
};
1229+
const result = generateSquidConfig(config);
1230+
const ipv4DenyPos = result.indexOf('http_access deny dst_ipv4');
1231+
const domainDenyPos = result.indexOf('http_access deny !allowed_domains');
1232+
expect(ipv4DenyPos).toBeGreaterThan(-1);
1233+
expect(domainDenyPos).toBeGreaterThan(-1);
1234+
expect(ipv4DenyPos).toBeLessThan(domainDenyPos);
1235+
});
1236+
1237+
it('should include IP deny rules even with no domains configured', () => {
1238+
const config: SquidConfig = {
1239+
domains: [],
1240+
port: defaultPort,
1241+
};
1242+
const result = generateSquidConfig(config);
1243+
expect(result).toContain('http_access deny dst_ipv4');
1244+
expect(result).toContain('http_access deny dst_ipv6');
1245+
});
1246+
1247+
it('should include IP deny rules in SSL Bump mode', () => {
1248+
const config: SquidConfig = {
1249+
domains: ['github.com'],
1250+
port: defaultPort,
1251+
sslBump: true,
1252+
caFiles: { certPath: '/tmp/cert.pem', keyPath: '/tmp/key.pem' },
1253+
sslDbPath: '/tmp/ssl_db',
1254+
};
1255+
const result = generateSquidConfig(config);
1256+
expect(result).toContain('http_access deny dst_ipv4');
1257+
expect(result).toContain('http_access deny dst_ipv6');
1258+
});
1259+
});
1260+
12011261
describe('Port validation in generateSquidConfig', () => {
12021262
it('should accept valid single ports', () => {
12031263
expect(() => {

src/squid-config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,13 @@ acl localnet src fe80::/10
533533
534534
${portAclsAndRules}
535535
536+
# Deny CONNECT to raw IP addresses (IPv4 and IPv6)
537+
# Prevents bypassing domain-based filtering via direct IP connections
538+
acl dst_ipv4 dstdom_regex ^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$
539+
acl dst_ipv6 dstdom_regex ^\\[?[0-9a-fA-F:]+\\]?$
540+
http_access deny dst_ipv4
541+
http_access deny dst_ipv6
542+
536543
${accessRulesSection}# Deny requests to unknown domains (not in allow-list)
537544
# This applies to all sources including localnet
538545
${denyRule}

0 commit comments

Comments
 (0)