[SQL] CIDR 목록 검색 예제(IPv4)

위키 엔진을 만들던 중 CIDR 차단 기능을 구현해야 하는데, 이걸 어찌할지 몰라 한참 고민하고 있었다. PHP 상에서 처리를 해야 하나 고민했지만 해답은 SQL에서 찾을 수 있었다.

문제 상황

나는 MariaDB를 운용하고 있었고, 테이블의 각 행에는 차단 대상으로 지정된 CIDR들이 등록되어 있었다.
| id | target_ip |
|—-|——————|
| 1 | 1.2.3.4/32 |
| 2 | 123.45.67.0/24 |
| 3 | 123.45.0.0/16 |
| 4 | 0.0.0.0/1 |

그리고 쿼리를 통해 접속한 IP가 차단 대상에 해당하는지 확인해야 한다. 그래서 특정 IP 주소를 포함하는 CIDR이 테이블 안에 있는지를 살펴야 한다.

검색 끝에 아래와 같은 쿼리를 찾았다. 원래는 CIDR로부터 범위를 추출하는 쿼리였지만 잘 개조해서 아래처럼 ‘대상 IP’를 포함하는 CIDR들을 추출할 수 있는 쿼리를 만들었다.

대상 IP를 123.45.67.8로 지정한 쿼리 결과:
| id |
| 2 |
| 3 |
| 4 |

대상 IP를 123.45.6.7로 지정한 쿼리 결과:
| id |
| 3 |
| 4 |

복붙해다 쓸거면 그냥 바로 아래 있는 걸 건져가서 적절히 수정하면 된다.

SELECT id FROM 테이블이름 WHERE INET_NTOA(INET_ATON( SUBSTRING_INDEX(target_ip, ‘/’, 1)) & 0xffffffff ^ ((0x1 << ( 32 – SUBSTRING_INDEX(target_ip, ‘/’, -1)) ) -1 )) <= ‘대상 IP’ AND INET_NTOA(INET_ATON( SUBSTRING_INDEX(target_ip, ‘/’, 1)) | ((0x100000000 >> SUBSTRING_INDEX(target_ip, ‘/’, -1) ) -1 )) >= ‘대상 IP’;

수정하기 편하라고 하이라이팅해뒀다.
SELECT id FROM 테이블이름 WHERE INET_NTOA(INET_ATON( SUBSTRING_INDEX(target_ip, ‘/’, 1)) & 0xffffffff ^ ((0x1 << ( 32SUBSTRING_INDEX(target_ip, ‘/’, -1)) ) -1 )) <= ‘대상 IP’ AND INET_NTOA(INET_ATON( SUBSTRING_INDEX(target_ip, ‘/’, 1)) | ((0x100000000 >> SUBSTRING_INDEX(target_ip, ‘/’, -1) ) -1 )) >= ‘대상 IP’;

참고 사이트: https://stackoverflow.com/questions/35562416/mysql-query-to-convert-cidr-into-ip-range

n n

n

n

n

MySQL query to convert CIDR into IP range

n

I have a table which contains a column as CIDR values like 1.0.85.128/25, I have 2 other columns (start_ip_range and end_ip_range), I want to populate those 2 column FROM CIDR column Java code might

n

stackoverflow.com

n

[PHP] mbstring 없이 한글 문자열 다루기

보통 PHP에서 한글 글자수를 센다던가, substr을 통해 자른다던가, 한글자씩 분리한다던가 할 때 php-mbstring 확장을 사용한다.

 

만약 자신이 만들고 있는 PHP 앱이 빠른 속도를 요구한다면, mbstring 확장을 떨쳐 내는 것은 이를 위한 좋은 방법이 될 수 있다. 실제로 해외의 여러 커뮤니티에서도 mb_strlen() 등은 매우 느리다고 이야기하고 있고, 그래서 구글링 끝에 얻은 대안 두 개를 소개한다.

 

1. iconv_

php-iconv 모듈에 속해 있는데 대부분 내장이라 뭘 따로 설치할 필요는 없다.

<?phpn$str = '가나다';necho microtime();nfor($i=0; $i<1000; ++$i){n    mb_strlen($str);n}necho microtime();nnecho microtime();nfor($i=0; $i<1000; ++$i){n    iconv_strlen($str);n}necho microtime();nnecho microtime();nfor($i=0; $i<1000; ++$i){n    count(preg_split('//u', $str));n}necho microtime();

속도를 측정해 보고 싶어서 다음과 같이 코드를 작성 후 실행하였다.

strlen을 측정하는 세 가지 방법을 사용하였다. 각 방법으로 1000회씩 실시해 그 소요시간의 차를 구해보겠다.

 

출력

0.15499200 1620953054

0.15879300 1620953054

0.15879800 1620953054

0.15966200 1620953054

0.15966400 1620953054

0.16053900 1620953054

 

분석해 보면, mb_strlen()은 0.003801초

iconv_strlen()은 0.000864초

count(preg_split(…))은 0.000875초가 걸렸다.

물론 하단에 서술하겠지만 count(preg_split()) 은 함수를 하나 더 써야해서 속도가 더 느릴 것이다.

이렇게 해 보면 mb_strlen이 얼마나 느린 함수인지 알 수 있다.

2. preg_split

출처:

stackoverflow.com/questions/3666306/how-to-iterate-utf-8-string-in-php

 

How to iterate UTF-8 string in PHP?

How to iterate a UTF-8 string character by character using indexing? When you access a UTF-8 string with the bracket operator $str[0] the utf-encoded character consists of 2 or more elements. For

stackoverflow.com

<?phpnfunction u_strlen($text){n    $result = array_slice(preg_split('//u', $text), 1, -2);n    return count($result);n}nnfunction u_substr($text, $offset, $len=null){n	$result = preg_split('//u', $text,-1,PREG_SPLIT_NO_EMPTY);n    return array_slice($result, $offset, $len);n}nnfunction u_strpos($haystatck, $needle, $offset=0){n	$split = preg_split('//u', $haystack,-1,PREG_SPLIT_NO_EMPTY);n    $result = array_slice($result, $offset);n    return array_search($needle, $result);n}nn$sample = '다람쥐 헌 쳇바퀴에 타고파';necho u_strlen($sample); // 14necho u_substr($sample, 1); // 람쥐 헌 쳇바퀴에 타고파necho u_strpos($sample, '쳇바퀴'); // 6n

 

 

 

정규식 함수를 쓰기 때문에 얼핏 보면 느려보일 수 있다. 그러나 메모리를 더 많이 먹을지언정 속도는 더 빨라졌다는 것이 해당 stackoverflow 페이지의 주 내용이다.

 

여기서 주의할 점은, PREG_SPLIT_NO_EMPTY 옵션을 주지 않으면 맨 앞과 맨 뒤에 공백이 생긴다.

[PHP 8] 시작과 끝 문자열 확인하기 – str_starts_with(), str_ends_with()

javascript에는 .startsWith()와 .endsWith() 라는 함수가 각각 시작과 끝 문자열을 반환해주는 기능을 한다.
그런데 PHP에는 이게 없어서 직접 기능을 구현해야 했다.

이 함수는 PHP 8에서 새롭게 추가된 기능인데, 문자열이 특정 문자로 시작하거나 끝나는지의 여부를 bool 형태로 반환한다.

str_starts_with( string $haystack, string $needle )nstr_ends_with( string $haystack, string $needle )nn// 예제n$a = 'abcdefghi';nif(str_starts_with($a, 'a')){n	echo 'Starts with a.';n}else{n	echo 'NOT Starts with a.';n}nnif(str_starts_with($a, 'b')){n	echo 'Starts with b.';n}else{n	echo 'NOT Starts with b.';n}n// Starts with a. NOT Starts with b. 가 출력된다.nn// 예제n$a = 'abcdefghi';nif(str_ends_with($a, 'i')){n	echo 'Ends with i.';n}else{n	echo 'NOT Ends with i.';n}nnif(str_ends_with($a, 'h')){n	echo 'Ends with h.';n}else{n	echo 'NOT Ends with h.';n}n// Ends with i. NOT Ends with h. 가 출력된다.

무료 DNS(네임서버) 소개 – DNSZi, LuaDNS

처음 웹 개발을 시작한 이후, 도메인 관리를 위해 오랫동안 무료 서비스인 DNSZi를 써 왔다. 이후, CAA 레코드와 DNSSEC 등을 적용할 마음이 생겨 AWS Route53이나 Cloudflare, DigitalOcean, Linode 등을 전전하다가 LuaDNS라는 곳을 찾게 되었다.
그래서 내가 지금까지 애용하고 있는(혹은 있던) 두 서비스를 소개하고자 한다.

1. DNSZi

https://dnszi.com

 

DNSZi – 무료 네임서버(dns) 관리 서비스! 무료파킹서비스! 무료포워딩서비스!

<!– –> 도메인 DNS관리를 웹에서 쉽게 하세요! – DNS를 관리하는데 시간과 비용이 드시나요?- 번거롭게 도메인 등록업체에 매번 DNS정보 수정을 요청하시나요?- DNS관리자에게 DNS정보 수정을 요청하

dnszi.com

PHP 코딩하는 사람은 한 번쯤 들어봤을 법한 PHPSCHOOL.com 과 동일한 기업에서 운영하고 있다. 이 사이트는 그래도 국내에서 무료 DNS 서비스로 유명한 편이다.
지원 레코드: A, CNAME, MX, TXT, SRV
기타 서비스: 웹 파킹, 웹 포워딩, DDNS

일단 회원가입을 하고 도메인을 추가하면 아래 사진처럼 네임서버가 배정된다.

5차 네임서버까지 모조리 지원하기 때문에 도메인 설정에서 네임서버 지정란을 꽉 채울 수 있다.
네임서버 번호(ns**.dnszi.com)는 도메인마다 랜덤하게 배정된다.

여기 보이는 상단 메뉴에서 원하는 기능을 선택해 설정할 수 있다. 각 메뉴마다 설정에 관한 자세한 설명이 적혀 있어 초심자도 쉽게 이용할 수 있다.

또한 TTL 관리가 가능한데, 아래 사진처럼 선택이 가능하다.

만약 도메인 기본 TTL에서 설정한 값을 동일하게 서브 도메인에 적용시키고 싶다면 서브 도메인의 TTL 설정을 ::TTL:: 로 하면 된다.

만약 DNS 레코드의 설정을 변경하고 싶다면, 설정값을 수정한 후 해당 레코드 우측에 있는 ‘E’ 버튼을 누르면 된다. (오른쪽에 X는 삭제버튼)
복수의 레코드를 동시에 수정하려면 수정한 각 레코드의 왼쪽 체크박스를 체크한 후, 아래쪽의 ‘선택수정’을 누르면 된다.

만약 자신의 서버가 필요로 하는 기능이 단순 ip연결과 같은 기본적인 기능이라면, DNSZi를 이용하면 된다. 레코드 수, Zone 수 등에 제약을 받지 않고 무제한으로 이용할 수 있기 때문이다.

2. LuaDNS

2-1. 기본 사용법

https://luadns.com

 

Bulk DNS Hosting – LuaDNS

Managed DNS service Anycast name servers distributed across the globe in 11 POPs (USA, Europe, Asia & Australia). The service was optimized for fast propagation of changes, servers are managed and constantly monitored by us. Control Panel A simple and fast

luadns.com

지원 레코드: A, AAAA, ALIAS, CAA, CNAME, DS, FORWARD, MX, NS, REDIRECT, SLAVE, SPF, SRV, SSHFP, TLSA, TXT
이 서비스의 강력한 장점은 바로 위와 같이 많은 종류의 레코드를 지원한다는 것이다. 물론 AWS Route53이나 Cloudflare가 더 많은 레코드를 지원했던 것으로 기억하지만, 이쪽은 유료이거나 기능 제약이 많아서 필자에게는 적합하지 않았다.

이 녀석은 해외 서비스이다. 일단 회원가입부터 하자.
로그인해서 zones 메뉴를 들어가 보면 이런 식으로 있을 것이다.
(필자는 사전에 도메인을 등록해 두었다.)

사진 우측에 ‘Add Zone’ 이라는 걸 누르면 도메인을 추가할 수 있다.

맨 위에 도메인을 적고, ‘Add Zone’을 누르자.
아래의 템플릿이나 레코드 복사 설정은 안 해도 된다.

그러면 이제 위 사진처럼 레코드 목록이 뜰 것이다. 사진에는 잘렸지만 왼쪽 하단에 보면 ‘Add New Record’ 라는 게 있다. 그걸 누르면 레코드가 추가된다.
host name 칸은 루트 도메인에 적용시키려면 빈 칸으로, 서브도메인에 적용시키려면 서브도메인 이름만 입력해주면 된다. (ex: a.abc.kr → ‘a’ 만 입력)

다 지정했으면 우측 하단의 Save를 누르자.

성공적으로 업데이트 되었다면 위와 같은 메시지가 뜬다. 참고로 저장이 완료되면 SOA – NS – (지정 레코드) 순으로 정렬이 되는데, 레코드 종류가 먼저 알파벳 순으로 정렬되고 그 다음 Name의 알파벳 순으로 정렬된다.
아래는 저장이 안 된 경우 뜨는 오류 메시지들이다.
invalid (ipv4/ipv6) address : A, AAAA 레코드의 Content에 잘못된 ip 주소를 기입한 경우
content: bad {내용}: {지정값} : {내용} 의 값이 잘못 지정된 경우
records: Found other records with (도메인). delegation, it should contain only NS and DS records : 한 도메인에 대해 DS 레코드를 여러 개 지정한 경우
Max records reached, please access account settings page to upgrade your package : 사용 가능한 레코드 수를 초과한 경우 (무료 서비스는 30개까지다. 레코드 왼쪽의 번호는 0부터 시작하므로 참고하자.)

양식을 틀려먹은 경우 아래와 같은 오류 페이지로 넘어가 버린다. 이 경우 수정한 내용을 모두 재 입력해야 하는 참극이 발생하므로 조심하자.

2-2. 요금제와 고급 기능

그런데, 위에서 눈치챘겠지만 서비스 자원을 무한하게 이용할 수 있는 건 아니다.
회원가입을 하면 무료 계정으로 등록되며, 3개의 도메인, 각 30개의 레코드를 사용할 수 있다. 기본으로 SOA와 NS 레코드 4개가 지정되므로 사실상 25개만 사용 가능한 것이다.

그래서 위 사진처럼 요금제가 있다. Zones 수 = 도메인 수 라고 봐도 무방하다.
유료 서비스는 개인의 필요에 따라 적절히 구매하면 되는데, 어지간히 큰 사이트가 아니면 굳이 살 필요는 없다.

그러면 왜 이 서비스를 이용하냐고? 그것은 이제 아래에 서술할 고급 기능 때문이다.

고급 기능 1. DNS API

LuaDNS는 DNS API를 지원하는데, 이게 무엇이냐 하면 저 사이트 외부에서 DNS 정보를 변경할 수 있도록 하는 것이다.
처음 보는 사람은 그래서 이게 왜 필요하냐고 묻곘지만 API를 제공하는 DNS 서비스를 특별히 찾는 이유는 바로 무료 wildcard 인증서를 자동으로 업데이트할 수 있기 때문이다.

여기서 wildcard 인증서가 무엇인지 간단히 설명하자면, 원래 도메인의 SSL 인증서는 각 도메인마다 따로 받아야 한다. 예를 들어 abc.kr, a.abc.kr, www.abc.kr 등 서브 도메인을 포함해서 모든 도메인이 각각 별개의 인증서를 발급받아야 하는 것이다. 그런데 이를 일일이 발급하고 관리하기란 매우 까다로운 일이다. 그래서 나온 게 모든 서브도메인에 대응할 수 있는 wildcard 인증서이다. *.abc.kr 의 인증서 하나로 a.abc.kr. b.abc.kr 등 모든 서브도메인의 인증서를 갈음할 수 있다. (물론 루트 도메인인 abc.kr은 따로 발급받아야 한다.)

그런데 Let’s Encrypt에서 발급한 인증서는 3개월이면 유효기한이 지나 쓸 수 없게 된다. 그래서 이를 자동으로 재발급 해 주는 프로그램이 있는데, 바로 acme.sh 라는 프로그램이다. 이 프로그램에 대한 자세한 사용법은 여기를 참고하자.

 

LuaDNS API를 이용한 무료도메인 와일드카드 SSL 인증서 발급 받기 > 작업실 | 우성짱의 NAS

1. 들어가며무료 DNS 서비스로 클라우드플레어를 추천하지만, 클라우드플레어는 Freenom의 무료도메인(ga,tk,ml,cf,gq)에 대해 API 인증을 지원하지 않습니다.따라서 와일드카드 SSL 발급이나 DDNS 기능

www.wsgvet.com

이 서비스는 보통 맨 처음에 언급했던 Cloudflare과 같은 대형 기업에서 주로 제공하는데, 굳이 거기를 놔두고 여기로 오는 이유는 freenom 같은 곳에서 발급하는 무료도메인은 Cloudflare를 이용할 수 없기 때문이다. 필자는 기능에 제약이 너무 많아서 이 서비스를 이용하기 시작했다.

고급 기능 2. DNSSEC

참고 문서

 

한국인터넷정보센터(KRNIC)

도메인 소개, 등록 및 사용, IP주소, AS번호, DNS 정보, 관련규정 제공

xn--3e0bx5euxnjje69i70af08bea817g.xn--3e0b707e

일단 DNSSEC가 뭔지는 위의 참고 문서를 한 번 읽어보자.

위에서 봤던 Zones 메뉴에서 저 빨갛게 동그라미 친 버튼을 누르면 DNSSEC 설정 페이지로 이동한다.

위에서 DNSSEC에 체크하고 Save를 누르면 아래 부분처럼 DNSEC(???) 메뉴가 나타난다.
여기까지 왔으면 DNS 서비스에서 할 일은 끝난 것이다. 루트 도메인에 대한 DS 레코드는 자동으로 적용되므로 따로 수정할 필요가 없다.
저기서 Key Tag, Algorithm, Digest Type, Digest와 저 빨간 원 안의 here를 누르면 나타나는 Public Key의 값을 들고 당신의 도메인 등록기관에 찾아가서 등록을 요청하자.

필자는 호스팅케이알을 이용하고 있어서 1:1 문의를 통해 등록했다. 위 사진처럼 알려주면 된다. DNSSEC 등록 방법은 도메인 기관마다 다르니 참고하자.

이렇게 두 서비스를 간단히 소개해 보았다.
본인의 서비스가 방금 소개한 두 고급 기능이 필요하다면 LuaDNS, 그렇지 않다면 DNSZi를 사용하면 되겠다.

PHP Lecture 2 – 변수

이번 글의 주제는 ‘변수’입니다.

‘변수’란 ‘어떤 관계나 범위 안에서 여러 가지 값으로 변할  있는 수’를 뜻합니다. (네이버 사전)

 

이제 PHP 상에서의 변수에 대해 알아보겠습니다.

 

1. 변수 선언

$variable = 'value';

$변수명 = ‘값’; 과 같이 새로운 변수를 선언할 수 있습니다.

 

2. 변수 재정의

1에서 설명드렸던 방법과 같이 $변수명 = ‘재정의 값’; 을 통해 변수를 재정의할 수 있습니다.

이렇게 하면 변수의 값이 변경됩니다.

 

3. 변수명

변수에 대해 짚고 넘어가야 할 점 중 하나는 변수명에 제약이 있다는 것입니다.

아래와 같은 이름의 변수는 사용할 수 없습니다.

// 변수명에 숫자 사용 불가n$12345 = '12345';nn// 변수명에 +, - 등의 연산자와 . , @ ! ' " 등은 사용 불가n$h(ell)o! = 'hello world';n$my-name = 'PRASEOD-';n$one+one = 'two';n$don'tusethis. = 'abcd';n...

4. 연산

변수는 연산자를 사용할 수 있습니다.

$a = 1;n$b = 2;necho $a + $b;n// 3이 출력된다.nnecho $a * $b;n// 2가 출력된다.

단순 연산도 가능하고, 괄호를 사용한 혼합 계산 역시 가능하다.

혹은 아래와 같이 계산할 수도 있다.

 

$cost = 100;n$cost += 10; // $cost = $cost + 10; 과 같은 계산necho $cost;n// 110이 출력된다.nn++$cost; // $cost의 값을 1 더하여 111 출력n--$cost; // $cost의 값을 다시 1 빼서 110 출력nn$pre = 'abc';n$suf = 'def';n$pre .= $suf; // $pre = $pre + $suf; 와 같은 계산n// abcdef가 출력된다.

위와 같이 활용 가능하다.

 

5. 문자열과의 혼합

문자열과 함께 쓸 수도 있다.

$v = '나는';nnecho $v.' 글을 씁니다.';necho "$v 글을 씁니다."; // 큰따옴표만 사용해야 함.n// 둘 다 '나는 글을 씁니다' 가 출력된다.nn$x = '글을';nnecho "나는 ".$x." 씁니다.";necho "나는 $x 씁니다."; // 큰따옴표만 사용해야 함.n// 둘 다 '나는 글을 씁니다' 가 출력된다.nn$z = '밥';nnecho "잡곡".$z;necho "잡곡{$z}";n// 둘 다 '잡곡밥' 이 출력된다.

이 정도면 기초적인 활용법은 끝났습니다.

이들을 자유자재로 활용할 수 있어야 합니다.

PHP Lecture 1 – 시작

PHP란 무엇인가?

 이 질문으로 시작해 보겠습니다.

PHP는 웹 분야에서 주로 쓰이는 프로그래밍 언어의 하나로, 원래 Perseonal Home Page Tools의 약자로, 현재는 PHP – Hypertext Preprocessor 의 약자로 사용하고 있습니다.

이 언어는 TIOBE에서 선정한 2020년 10월 기준 검색어 점유율 상위 20개 프로그래밍 언어 중 8위를 차지했습니다.

 

PHP의 주 특징 중 하나는 바로 서버 사이드 언어라는 것입니다. 본 블로그에서는 현재 최신 버전인 PHP 7.4를 기준으로 포스팅을 작성할 예정입니다.

 

PHP 시작하기

웹 서버에서 PHP를 사용하기 위해서는 서버에 PHP가 설치되어 있어야 합니다.

Ubuntu 20.04 / NGINX 기준으로 설명하겠습니다. NGINX는 설치되어 있다고 가정합니다.

 

1. CLI에 접속해 다음 명령어로 PHP를 설치합니다.

> $ sudo apt-get -y install php[버전]

PHP7.4 기준으로 다음과 같이 입력합니다.

> $ sudo apt-get -y install php7.4

같이 설치되는 패키지는 그냥 설치하시면 됩니다.

 

2. NGINX 설정 파일의 서버 영역에 다음 부분을 추가합니다.

server {n	...n  	index index.php index.html;n	...nn	# pass PHP scripts to FastCGI servern	#n	location ~ \.php$ {n		include snippets/fastcgi-php.conf;n		include fastcgi_params;	nn	#	# With php-fpm (or other unix sockets):n		fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;n	#	# With php-cgi (or other tcp sockets):n	#	fastcgi_pass unix:/run/php/php7.4-fpm.sock;n		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;n		fastcgi_read_timeout 300;n	...n	}n}

혹시 PHP 소켓 위치가 다르다면 php7.4-fpm.sock의 위치를 수정해 주시면 됩니다.

index에 index.php를 추가하셔야 디렉토리로 접근했을 때 자동으로 index.php를 로드하게 됩니다.

이 단계를 거치지 않는다면 URL을 통해 PHP 파일로 접근했을 때 PHP 파일을 다운로드하는 창이 뜰 것입니다.

 

설정 후 다음 명령어를 입력하여 NGINX 설정 파일의 오류를 확인합니다.

> $ nginx -t

 

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok

nginx: configuration file /etc/nginx/nginx.conf test is successful

실행 후 이러한 문구가 보였다면 정상입니다.

 

설정 후 다음 명령어를 입력하여 NGINX를 다시 시작합니다.

> $ sudo systemctl restart nginx

 

코드를 써보자

 

PHP는 스크립트 실행 영역을 직접 정의할 수 있습니다. 정의는 아래와 같이 이루어집니다.

 

<?phpn // Write Something Here...n?>

 

<?php와 ?> 사이에 PHP 코드를 작성하면 됩니다.

다음 글에서는 PHP 코드에 대해 알아보겠습니다.