fix(header): security fix for header values that include newlines

Newlines in header values will now be replaced with spaces when being
written to strings or to sockets. This prevents headers that are built
from user data to smuggle unintended headers or requests/responses.

Thanks to @skylerberg for the responsible reporting of this issue, and
helping to keep us all safe!

BREAKING CHANGE: This technically will cause code that a calls
  `SetCookie.fmt_header` to panic, as it is no longer to properly write
  that method. Most people should not be doing this at all, and all
  other ways of printing headers should work just fine.

  The breaking change must occur in a patch version because of the
  security nature of the fix.
This commit is contained in:
Sean McArthur
2017-01-19 12:53:08 -08:00
parent 7d400398ab
commit 8e790831c1
3 changed files with 146 additions and 35 deletions

View File

@@ -4,7 +4,7 @@ use std::fmt;
use std::str::from_utf8;
use super::cell::{OptCell, PtrMapCell};
use header::{Header, Raw};
use header::{Header, MultilineFormatter, Raw};
#[derive(Clone)]
@@ -90,6 +90,29 @@ impl Item {
None => parse::<H>(self.raw.as_ref().expect("item.raw must exist")).ok()
}.map(|typed| unsafe { typed.downcast_unchecked() })
}
pub fn write_h1(&self, f: &mut MultilineFormatter) -> fmt::Result {
match *self.raw {
Some(ref raw) => {
for part in raw.iter() {
match from_utf8(&part[..]) {
Ok(s) => {
try!(f.fmt_line(&s));
},
Err(_) => {
error!("raw header value is not utf8, value={:?}", part);
return Err(fmt::Error);
}
}
}
Ok(())
},
None => {
let typed = unsafe { self.typed.one() };
typed.fmt_multi_header(f)
}
}
}
}
#[inline]
@@ -99,24 +122,3 @@ fn parse<H: Header>(raw: &Raw) -> ::Result<Box<Header + Send + Sync>> {
h
})
}
impl fmt::Display for Item {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self.raw {
Some(ref raw) => {
for part in raw.iter() {
match from_utf8(&part[..]) {
Ok(s) => try!(f.write_str(s)),
Err(e) => {
error!("raw header value is not utf8. header={:?}, error={:?}",
part, e);
return Err(fmt::Error);
}
}
}
Ok(())
},
None => fmt::Display::fmt(&unsafe { self.typed.one() }, f)
}
}
}