Browser가 DNS Query를 직접 한다는 것을 알고 계셨을까요?
엄밀히 말하면 글 제목에서 알 수 있듯이 Chromium 기반의 Browser만 DNS Query를 직접 수행합니다.
* Chromium Based Browser는 Chrome, Edge, Opera, etc...입니다.
Bing에게 문의해 본 결과, 직접 한다는 것을 확인해 주었는데요. 🤔

실제로 검증하고자 아래와 같이 테스트해 보았습니다.
1. 제 컴퓨터의 DNS Cache 초기화 (DNS Flush)

2. Process Monitor로 4개의 Browser(Edge, Opera, Chrome, Firefox)에 대한 DNS Query 주체를 확인해 봤습니다.
Process Monitor로 아래와 같이 Filtering 하여 확인해 본 결과,
- Path contains {Browser product}
- Path ends with :53 (DNS uses both TCP and UDP port 53)
Firefox = DNS Query를 OS단에서 수행했습니다.
(Firefox를 Filtering 한 결과 데이터를 확인할 수 없었습니다.)

즉, FireFox Browser는 OS 단(svchost.exe)에서 수행함을 알 수 있었습니다.

반면에 Chromium Based Browser(Chrome, Edge, Opera)는 아래와 같이 DNS Query를 직접 수행하는 것을 검증할 수 있었습니다.

그렇다면 왜 DNS Query를 직접 수행하는 것일까요?
정확한 답은 찾을 수는 없었지만, 개인적인 생각으로는 아래와 같이 보다 나은 보안 환경을 제공하고자 직접 수행한다고 생각합니다.


출처: Chromium Blog: A safer and more private browsing experience with Secure DNS
위 테스트를 통해 Chromium Based Browser는 DNS Query를 직접 수행한다는 것을 확인할 수 있었습니다.
(Bing이 나름 똑똑하긴 하네요.😎)
번외로 DNS는 TCP와 UDP 모두 사용할 수 있다는 것은 알고 계셨을 텐데요.

DNS는 UDP와 TCP 모두 사용할 수 있지만 주로 UDP를 사용합니다.
그렇다면 어느 조건에 UDP에서 TCP로 전환될까요?
Chormium Open source를 확인해 본 결과 여러 조건이 있습니다.
(Open Source를 공부하며 개인적으로 도식화해 봤습니다.)

모든 내용을 설명하기엔 방대한 양이기에 low_entropy에 한해서 정리하겠습니다.
보다 자세한 내용을 확인하시고 싶으시면 아래 오픈소스 링크에서 확인 가능합니다.
- dns_udp_tracker.cc - Chromium Code Search
위 순서도에서 확인할 수 있듯이, low_entropy()가 True로 변경되면 DNS를 UDP에서 TCP로 시도하게 됩니다.
= Code Level에서 low_entropy()가 Ture로 되는 조건을 확인하면 UDP에서 TCP로 전환되는 조건을 알 수 있습니다.
실제 Code상으로는 Reference를 확인한 결과 아래와 같이 총 4가지 조건이 있습니다.

1. 재사용된 포트의 수가 3회 이상인 경우
//dns_udp_tracker.cc
void DnsUdpTracker::RecordQuery(uint16_t port, uint16_t query_id) {
PurgeOldRecords();
int reused_port_count = base::checked_cast<int>(
base::ranges::count(recent_queries_, port, &QueryData::port));
// 재사용된 Client의 Port 수가 포트재사용임계값(3) 이상인 경우
if (reused_port_count >= kPortReuseThreshold && !low_entropy_) {
low_entropy_ = true;
RecordLowEntropyUma(LowEntropyReason::kPortReuse);
}
SaveQuery({port, query_id, tick_clock_->NowTicks()});
}
//dns_udp_tracker.h
static constexpr int kPortReuseThreshold = 3;
Windows에서 Dynamic port Range는 49,152 ~ 65,536이며 사용가능한 port의 수는16,384입니다.잘 아시겠지만, 해당 dynamic port Range를 Udp만 사용하는 것은 아닙니다.)

즉 UDP에서 TCP로 전환될 확률(3회 이상 재사용될 확률)은3.92566 e-05 = 0.0000392566입니다.입니다
( …/entropy_threshold_calculator.cc · Gerrit Code Review (googlesource.com))

2. INSUFFICIENT_RESOURCES (Server 측의 자원 부족을 의미합니다.)
//dns_udp_tracker.cc
void DnsUdpTracker::RecordConnectionError(int connection_error) {
if (!low_entropy_ && connection_error == ERR_INSUFFICIENT_RESOURCES) {
// On UDP connection, this error signifies that the process is using an
// unreasonably large number of UDP sockets, potentially a deliberate
// attack to reduce DNS port entropy.
low_entropy_ = true;
RecordLowEntropyUma(LowEntropyReason::kSocketLimitExhaustion);
}
}
//net_error_list.h
// There were not enough resources to complete the operation.
NET_ERROR(INSUFFICIENT_RESOURCES, -12)
즉, DNS 운영 측 Server의 자원이 부족할 경우 UDP에서 TCP로 전환됩니다.
3. 인지된 ID 불일치.
- 인지된 ID 불일치 수가 127 회인 경우
4. 미 인지된 ID 불일치
- 미 인지된 ID 불일치 수가 7 회 인 경우
//dns_udp_tracker.cc
void DnsUdpTracker::SaveIdMismatch(uint16_t id) {
// No need to track mismatches if already flagged for low entropy.
if (low_entropy_)
return;
base::TimeTicks now = tick_clock_->NowTicks();
base::TimeTicks time_cutoff = now - kMaxRecognizedIdAge;
bool is_recognized =
base::ranges::any_of(recent_queries_, [&](const auto& recent_query) {
return recent_query.query_id == id && recent_query.time >= time_cutoff;
});
if (is_recognized) {
DCHECK_LT(recent_recognized_id_hits_.size(),
kRecognizedIdMismatchThreshold);
if (recent_recognized_id_hits_.size() ==
kRecognizedIdMismatchThreshold - 1) {
low_entropy_ = true;
RecordLowEntropyUma(LowEntropyReason::kRecognizedIdMismatch);
return;
}
DCHECK(recent_recognized_id_hits_.empty() ||
now >= recent_recognized_id_hits_.back());
recent_recognized_id_hits_.push_back(now);
} else {
DCHECK_LT(recent_unrecognized_id_hits_.size(),
kUnrecognizedIdMismatchThreshold);
if (recent_unrecognized_id_hits_.size() ==
kUnrecognizedIdMismatchThreshold - 1) {
low_entropy_ = true;
RecordLowEntropyUma(LowEntropyReason::kUnrecognizedIdMismatch);
return;
}
DCHECK(recent_unrecognized_id_hits_.empty() ||
now >= recent_unrecognized_id_hits_.back());
recent_unrecognized_id_hits_.push_back(now);
}
}
//dns_udp_tracker.h
// Numbers of ID mismatches required to set the |low_entropy_| flag. Also
// serves as the max number of mismatches to be recorded, as no more entries
// are recorded after setting the flag.
static constexpr size_t kUnrecognizedIdMismatchThreshold = 8;
static constexpr size_t kRecognizedIdMismatchThreshold = 128;
이외에 더 조건이 있지만 공유드리는 취지와 맞지 않아서 생략하겠습니다.
위 내용을 다시 요약해 보자면,
1. Chromium Based Browser는 DNS Query를 직접 수행합니다.
2. DnsUdpTracker가 Client, Server 측을 체크하며 특정 임계치를 넘으면 UDP에서 TCP로 DNS Query를 수행합니다.
긴 글 읽어주셔서 감사합니다.🫡
해당 내용에 관련하여 피드백은 환영입니다.
댓글