iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 23
0

前言

今天來談談瀏覽器的安全性連線,也就是講講 HTTPS 是甚麼以及怎麼做?
老樣子我們又回到講解 Servo 的時候囉~

What?

相信大家瀏覽網站的時候常常會看到「綠色的?」,點擊一下他會告訴你是安全連線

以 Firefox 來說它就會告訴你:

當 Firefox 連線到一個使用安全連線的網站(網址以 "https://" 開頭)時,Firefox 會檢驗網站憑證的正確性以及連線的加密強度以確保您的隱私。如果憑證無法被驗證,或連線使用了不夠強的加密演算法,Firefox 會中斷與網站的連線

想暸解 HTTPS 更多的話可以看 維基百科這篇。礙於篇幅就不重述。

How?

Servo 在網路連線上的實現都在 servo/components/net/。裡面包含處理 http、https 協定、headers、cookies、resources 等等。

而今天的重點是瀏覽器的安全性連線,那麼重點當然就在 HTTPS 和 SSl 的連線部分囉!

相關的程式碼在 /components/net/connector.rs,並不會很長,大家可以點進去看看。(以下的範例碼我有把原始碼重新排版過)

這邊程式的實現是處理「連線」,同時可以應付 HTTP 和 HTTPS。而如果是 HTTPS 的連線就用 SSL 的 Client 去包裝。

這邊先定義連線的物件

pub struct HttpsConnector {
    ssl: OpensslClient,
}

impl HttpsConnector {
    fn new(ssl: OpensslClient) -> HttpsConnector {
        HttpsConnector {
            ssl: ssl,
        }
    }
}

這邊是在連線時決定要採用 HTTP 還是 HTTPS 協議

impl NetworkConnector for HttpsConnector {
    type Stream = HttpsStream<<OpensslClient as SslClient>::Stream>;

    fn connect(&self, host: &str, port: u16, scheme: &str) 
        -> HyperResult<Self::Stream>
    {
        if scheme != "http" && scheme != "https" {
            return Err(HyperError::Io(
                io::Error::new(io::ErrorKind::InvalidInput,
                               "Invalid scheme for Http")));
        }

        // Perform host replacement when making the actual 
        // TCP connection.
        let addr = &(&*replace_host(host), port);
        let stream = HttpStream(TcpStream::connect(addr)?);

        if scheme == "http" {
            Ok(HttpsStream::Http(stream))
        } else {
            // Do not perform host replacement on the host that is
            // used for verifying any SSL certificate encountered.
            self.ssl.wrap_client(stream, host).map(HttpsStream::Https)
        }
    }
}

對連線方式不熟的話,建議把本文一開始建議閱讀資料看完。
使用 SSL 的時候會用到 TLS,所以這邊有 SslMethod::tls(),此外還要檢查憑證(CA)
都沒問題就可以順利用 SSL 傳輸囉!

pub type Connector = HttpsConnector;

pub fn create_ssl_client(ca_file: &PathBuf) -> OpensslClient {
    let mut ssl_connector_builder = 
        SslConnectorBuilder::new(SslMethod::tls()).unwrap();
    {
        let context = ssl_connector_builder.builder_mut();
        context.set_ca_file(ca_file).expect("could not set CA file");
        context.set_cipher_list(DEFAULT_CIPHERS)
            .expect("could not set ciphers");
        context.set_options(
            SSL_OP_NO_SSLV2 | SSL_OP_NO_SSLV3 | SSL_OP_NO_COMPRESSION);
    }
    let ssl_connector = ssl_connector_builder.build();
    OpensslClient::from(ssl_connector)
}

pub fn create_http_connector(ssl_client: OpensslClient) 
-> Pool<Connector> {
    let https_connector = HttpsConnector::new(ssl_client);
    Pool::with_connector(Default::default(), https_connector)
}

希望對大家有幫助,我們明天見!


關於作者

劉安齊

軟體工程師,熱愛寫程式,更喜歡推廣程式讓更多人學會


上一篇
瀏覽器開發進階實戰(四)font-family
下一篇
瀏覽器平行化
系列文
來做個網路瀏覽器吧!Let's build a web browser!35

尚未有邦友留言

立即登入留言