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:
@ -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"));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user