1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-01 16:55:08 +02:00

Fix audit issue logging by default peer address (#1485)

* Fix audit issue logging by default peer address

By default log format include remote address that is taken from headers.
This is very easy to replace making log untrusted.

Changing default log format value `%a` to peer address we are getting
this trusted data always. Also, remote address option is maintianed and
relegated to `%{r}a` value.

Related  kanidm/kanidm#191.

* Rename peer/remote to remote_addr/realip_remote_addr

Change names to avoid naming confusions. I choose this accord to Nginx
variables and
[ngx_http_realip_module](https://nginx.org/en/docs/http/ngx_http_realip_module.html).

Add more specific documentation about security concerns of using Real IP
in logger.

* Rename security advertise header in doc

* Add fix audit issue logging by default peer adress to changelog

Co-authored-by: Rob Ede <robjtede@icloud.com>
This commit is contained in:
pando85
2020-05-15 02:07:27 +02:00
committed by GitHub
parent 92ce975d87
commit 4fc99d4a6f
3 changed files with 98 additions and 27 deletions

View File

@ -72,12 +72,21 @@ use crate::HttpResponse;
///
/// `%U` Request URL
///
/// `%{r}a` Real IP remote address **\***
///
/// `%{FOO}i` request.headers['FOO']
///
/// `%{FOO}o` response.headers['FOO']
///
/// `%{FOO}e` os.environ['FOO']
///
/// # Security
/// **\*** It is calculated using
/// [`ConnectionInfo::realip_remote_addr()`](../dev/struct.ConnectionInfo.html#method.realip_remote_addr)
///
/// If you use this value ensure that all requests come from trusted hosts, since it is trivial
/// for the remote client to simulate been another client.
///
pub struct Logger(Rc<Inner>);
struct Inner {
@ -301,7 +310,7 @@ impl Format {
/// Returns `None` if the format string syntax is incorrect.
pub fn new(s: &str) -> Format {
log::trace!("Access log format: {}", s);
let fmt = Regex::new(r"%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrUsbTD]?)").unwrap();
let fmt = Regex::new(r"%(\{([A-Za-z0-9\-_]+)\}([aioe])|[atPrUsbTD]?)").unwrap();
let mut idx = 0;
let mut results = Vec::new();
@ -315,6 +324,11 @@ impl Format {
if let Some(key) = cap.get(2) {
results.push(match cap.get(3).unwrap().as_str() {
"a" => if key.as_str() == "r" {
FormatText::RealIPRemoteAddr
} else {
unreachable!()
},
"i" => FormatText::RequestHeader(
HeaderName::try_from(key.as_str()).unwrap(),
),
@ -362,6 +376,7 @@ pub enum FormatText {
Time,
TimeMillis,
RemoteAddr,
RealIPRemoteAddr,
UrlPath,
RequestHeader(HeaderName),
ResponseHeader(HeaderName),
@ -458,7 +473,15 @@ impl FormatText {
*self = FormatText::Str(s.to_string());
}
FormatText::RemoteAddr => {
let s = if let Some(remote) = req.connection_info().remote() {
let s = if let Some(ref peer) = req.connection_info().remote_addr() {
FormatText::Str(peer.to_string())
} else {
FormatText::Str("-".to_string())
};
*self = s;
}
FormatText::RealIPRemoteAddr => {
let s = if let Some(remote) = req.connection_info().realip_remote_addr() {
FormatText::Str(remote.to_string())
} else {
FormatText::Str("-".to_string())
@ -549,6 +572,7 @@ mod tests {
header::USER_AGENT,
header::HeaderValue::from_static("ACTIX-WEB"),
)
.peer_addr("127.0.0.1:8081".parse().unwrap())
.to_srv_request();
let now = OffsetDateTime::now_utc();
@ -570,6 +594,7 @@ mod tests {
};
let s = format!("{}", FormatDisplay(&render));
assert!(s.contains("GET / HTTP/1.1"));
assert!(s.contains("127.0.0.1"));
assert!(s.contains("200 1024"));
assert!(s.contains("ACTIX-WEB"));
}
@ -598,4 +623,36 @@ mod tests {
let s = format!("{}", FormatDisplay(&render));
assert!(s.contains(&format!("{}", now.format("%Y-%m-%dT%H:%M:%S"))));
}
#[actix_rt::test]
async fn test_remote_addr_format() {
let mut format = Format::new("%{r}a");
let req = TestRequest::with_header(
header::FORWARDED,
header::HeaderValue::from_static("for=192.0.2.60;proto=http;by=203.0.113.43"),
)
.to_srv_request();
let now = OffsetDateTime::now_utc();
for unit in &mut format.0 {
unit.render_request(now, &req);
}
let resp = HttpResponse::build(StatusCode::OK).force_close().finish();
for unit in &mut format.0 {
unit.render_response(&resp);
}
let entry_time = OffsetDateTime::now_utc();
let render = |fmt: &mut Formatter<'_>| {
for unit in &format.0 {
unit.render(fmt, 1024, entry_time)?;
}
Ok(())
};
let s = format!("{}", FormatDisplay(&render));
println!("{}", s);
assert!(s.contains("192.0.2.60"));
}
}