Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTPClient doesn't send the URL in HTTPS #1941

Closed
J6B opened this issue Apr 19, 2016 · 31 comments
Closed

HTTPClient doesn't send the URL in HTTPS #1941

J6B opened this issue Apr 19, 2016 · 31 comments

Comments

@J6B
Copy link

J6B commented Apr 19, 2016

Basic Infos

Trying to make a GET to /something and receive a request for / only on web server side.

Hardware

Hardware: ESP-01
Core Version: 2.2.0

Description

My sketch do a GET using HTTPClient.
I was in release 2.0.0 and I just updated to 2.2.0 release (did not try in 2.1.0)
My sketch was able to send GET for HTTP and HTTPS URI using 2.0.0 release.
Since I updated to 2.2.0, HTTP works almost fine (getting frequent -11 return code while my server answering bellow 5sec).

But the issue is while i'm trying to send same GET request using HTTPS, My web server receive a wrong header requesting for / only.

Exemple:
I request for https://192.168.1.123/plugins/teleinfo/core/php/jeeTeleinfo.php?api=YQFMNcxGJ52XSrHYcF3U&ADCO=050522038502&HCHP=072686874&PAPP=00730
My apache2 log contains this:
192.168.1.104 - - [19/Apr/2016:11:57:21 +0200] "GET / HTTP/1.0" 400 0 "-" "-"

Settings in IDE

Module: Generic ESP8266 Module
Flash Size: 1MB
CPU Frequency: 80Mhz
Flash Mode: dio
Flash Frequency: 40Mhz
Upload Using: OTA
Reset Method: ck

Sketch

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

void setup() {
 //init Wifi
}

void loop() {

String completeURI = "https://192.168.1.123/plugins/teleinfo/core/php/jeeTeleinfo.php?api=YQFMNcxGJ52XSrHYcF3U&ADCO=050522038502&HCHP=072686874&PAPP=00730";

  //send HTTP request
  HTTPClient http;
  http.begin(completeURI);
  requestResult = http.GET();
  http.end();

}




<bountysource-plugin>

---
Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/33106058-httpclient-doesn-t-send-the-url-in-https?utm_campaign=plugin&utm_content=tracker%2F14245935&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F14245935&utm_medium=issues&utm_source=github).
</bountysource-plugin>
@J6B
Copy link
Author

J6B commented Apr 19, 2016

I just tried with 2.1.0 and the same code works fine to reach server using HTTPS

@wouterds
Copy link

I can confirm that I can't do any calls to https endpoints using the ESP8266HTTPClient.h lib.

Eg this code:

  Serial.print("API Call to: https://www.reddit.com/iphone.json");
  Serial.println();
    
  HTTPClient http;
  
  int beginResult = http.begin("https://www.reddit.com/iphone.json");
  http.addHeader("Content-Type", "text/plain");
  
  int httpCode = http.POST("Message from ESP8266");
  String response = http.getString();
  
  Serial.print("beginResult: ");
  Serial.print(beginResult);
  Serial.println();
  
  Serial.print("http: ");
  Serial.print(httpCode);
  Serial.println();
  
  Serial.print("response: ");
  Serial.println(response);
  Serial.println();
  
  http.end();

Should return 404 for the status code and {"message": "Not Found", "error": 404} for the response body. Instead it returns -1 and no contents (connection refused). This happens for all https endpoints.

@J6B
Copy link
Author

J6B commented Oct 12, 2017

Hi, this is because you need now to provide website certificat thumbprint in http.begin for https website.

http.begin("https://www.reddit.com/iphone.json","f8d1965323111e86e6874aa93cc7c52969fb22bf");
(I put the thumbprint of www.reddit.com :-) )

@wouterds
Copy link

wouterds commented Oct 12, 2017

What if it changes (thinking about tools like Letsencrypt and Cloudflare that generate free certificates on the fly that are only valid for short periods of time)?

@igrr
Copy link
Member

igrr commented Oct 12, 2017 via email

@J6B
Copy link
Author

J6B commented Oct 12, 2017

Thanks igrr :-)

@wouterds
Copy link

wouterds commented Oct 12, 2017

Alright, good to know @igrr, thanks :-)!

Another question: is there a way to ignore it if you don't care about it? Eg like the unix command wget that has an option --no-check-certificate?

@igrr
Copy link
Member

igrr commented Oct 12, 2017 via email

@wouterds
Copy link

I guess I'll do that for now, thanks!

@hutch120
Copy link

Just wondering if anyone got this going? If so, any chance you can publish the relevant bits of the code to change in HTTPClient.cpp? This is what I tried...

I can see the entry point for https is:

bool HTTPClient::begin(String url, String httpsFingerprint)

So I detect the url using url.startsWith("https:"), then call client.begin with a fake string param like so:

  if (url.startsWith("https:")) {
    client.begin(url, "INSECURE_HTTPS"); //Specify request destination for HTTPS
  } else{    
    client.begin(url);  //Specify request destination for HTTP
  }

And tried changing this:

_transportTraits = TransportTraitsPtr(new TLSTraits(httpsFingerprint));

to this:

_transportTraits = TransportTraitsPtr(new TransportTraits());

Few other things I tried, but that didn't work.

  • return true on the verify function
  • delete the whole verify code block.

Any tips?

@hutch120
Copy link

@wouterds Did you get this going? Any chance you can post the relevant changes? (See my attempts above)

@wouterds
Copy link

wouterds commented Oct 19, 2017 via email

@TTnsp
Copy link

TTnsp commented Oct 19, 2017

@hutch120 : I have the same problem. So, to connect in https without certificat (fingerprint), i change the fonction "verify" in TLSTraits class in ESP8266HTTPClient.cpp. I made this fonction return always true, and that work fine.

@hutch120
Copy link

Hi @igrr, Any chance you could help @wouterds and I out on this one? We've both had a crack at modifying the source in ESP8266HTTPClient.cpp but are coming up short.

Any tips?

@zanechua
Copy link

@hutch120

Actually @TTnsp already provided the answer.

Here's the modified code portion in the ESP8266HTTPClient.cpp file

class TLSTraits : public TransportTraits
{
public:
    TLSTraits(const String& fingerprint) :
        _fingerprint(fingerprint)
    {
    }

    std::unique_ptr<WiFiClient> create() override
    {
        return std::unique_ptr<WiFiClient>(new WiFiClientSecure());
    }

    bool verify(WiFiClient& client, const char* host) override
    {
        auto wcs = static_cast<WiFiClientSecure&>(client);
        //Always return true to never validate certificate.
        return true;
    }

@hutch120
Copy link

@zanechua Thanks... I'm pretty sure I tried returning true from the verify function, I even made a note that I tried it in my post.

I'll have another crack.... did you try returning true from the verify function @wouterds ?

@zanechua
Copy link

zanechua commented Nov 15, 2017

@hutch120

It works for me though. I'm using LetsEncrypt on my https server. It was a POST Request.

Did you modify the library directly? I actually copied them into my sketch folder and change the headers to use the local sketch libraries instead.

@TTnsp
Copy link

TTnsp commented Nov 15, 2017

@zanechua

I'm in the same case of you and i copied the librairie into my sketch.

@iamalonso
Copy link

iamalonso commented Nov 26, 2017

@zanechua Actually I'm trying to send a POST request from my Wemos d1 mini board to Mlab API via https and I have your same problem. I made a local copy of ESP8266HTTPClient.cpp into my sketch and I modified the verify function to return true but it didn't work.

@zanechua
Copy link

@alonsocarvajal

Not sure if I can help but what error are you getting?

@iamalonso
Copy link

@zanechua

Below I attach a portion of my code.

void loop() {

  // Get temperature and humidity
  float temperature = dht.readTemperature();
  float humidity = dht.readHumidity();

  // Creating JSON object
  JsonObject& root = jsonBuffer.createObject();
  root["temperature"] = temperature;
  root["humidity"] = humidity;
  size_t requestBodySize = root.measureLength() + 1;
  char requestBody[requestBodySize];
  root.printTo(requestBody, requestBodySize);

  // Starting  POST request 
  int httpBeginCode = http.begin("https://api.mlab.com/api/1/databases/MyDB/collections/MyColl?apiKey=XXXXXXXXXXXXXXXXXXXXXXXXX");
  http.addHeader("Content-Type", "application/json");
  int httpPOSTCode = http.POST(requestBody);

  // Printing responses
  Serial.println(httpBeginCode);
  Serial.println(httpPOSTCode);

  // Ending http request
  http.end();
  delay(3000);
}

The return codes that I'm receiving in httpBeginCode and httpPOSTCode variables are 0 and -1 respectively.

Remember that actually I modified the verify function located on ESP8266HTTPClient.cpp file to return true (see the next code).

bool verify(WiFiClient& client, const char* host) override
    {
        auto wcs = static_cast<WiFiClientSecure&>(client);
        //return wcs.verify(_fingerprint.c_str(), host);
        return true;
    }

@zanechua
Copy link

Could you try this line with your post request instead?

int httpBeginCode = http.begin("https://api.mlab.com/api/1/databases/MyDB/collections/MyColl?apiKey=XXXXXXXXXXXXXXXXXXXXXXXXX", "imaginarykey");

@iamalonso
Copy link

iamalonso commented Nov 27, 2017

@zanechua

I tried that alternative and now the return codes that I'm receiving in httpBeginCode and httpPOSTCode variables are 1 and -1 respectively. I suppose that now the http.begin() its ok but the http.POST() request stills fail.

Additionally I tried a GET request to get data from MLAB's and NASA's API but I received -1 as a return code. The code for this example is:

int httpBeginCode = http.begin("https://api.nasa.gov/planetary/apod?api_key=NNKOjkoul8n1CH18TWA9gwngW1s1SmjESPjNoUFo", "2E BD F3 9F C1 1D BC 49 96 25 6A DC 32 EB FE 2B F0 32 70 50");
int httpGETCode = http.GET();
  
// Print responses
Serial.println(httpBeginCode);
Serial.println(httpGETCode);
  
// Printing data
Serial.println(http.getString());
  
// Ending http request
http.end();
delay(3000);

In this occasion the return code httpBeginCode and httpGETCode are 1 and -1 respectively.

Therefore, in both cases (POST and GET request) the http.begin() is successful but the http.POST() and http.GET() methods are fail.

Note: I've also tried removing the return true in verify method into ESP8266HTTPClient.cpp file.

bool verify(WiFiClient& client, const char* host) override
    {
        auto wcs = static_cast<WiFiClientSecure&>(client);
        //return wcs.verify(_fingerprint.c_str(), host);
        return true;
    }
bool verify(WiFiClient& client, const char* host) override
    {
        auto wcs = static_cast<WiFiClientSecure&>(client);
        return wcs.verify(_fingerprint.c_str(), host);
        //return true;
    }

Note 2: The fingerprint of Nasa API was obtained from https://www.grc.com/fingerprints.htm.

@iamalonso
Copy link

Hello @igrr, I've been trying to get data from NASA's API using the example HTTPSRequest.ino but I can not succeed. The same happens when I send a POST request to MLAB's API (see the post above).

My code based on HTTPSRequest.ino is:

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

const char* ssid = "10";
const char* password = "XXXXXXX";
const char* host = "api.nasa.gov";
const int httpsPort = 443;

// Use web browser to view and copy
// SHA1 fingerprint of the certificate
const char* fingerprint = "2E BD F3 9F C1 1D BC 49 96 25 6A DC 32 EB FE 2B F0 32 70 50";


void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.print("connecting to ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Use WiFiClientSecure class to create TLS connection
  WiFiClientSecure client;
  Serial.print("connecting to ");
  Serial.println(host);
  if (!client.connect(host, httpsPort)) {
    Serial.println("connection failed");
    return;
  }

  if (client.verify(fingerprint, host)) {
    Serial.println("certificate matches");
  } else {
    Serial.println("certificate doesn't match");
  }

  String url = "/planetary/apod?api_key=NNKOjkoul8n1CH18TWA9gwngW1s1SmjESPjNoUFo";
  Serial.print("requesting URL: ");
  Serial.println(url);

  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "User-Agent: BuildFailureDetectorESP8266\r\n" +
               "Connection: close\r\n\r\n");

  Serial.println("request sent");
  while (client.connected()) {
    String line = client.readStringUntil('\n');
    if (line == "\r") {
      Serial.println("headers received");
      break;
    }
  }
  String line = client.readStringUntil('\n');
  if (line.startsWith("{\"state\":\"success\"")) {
    Serial.println("esp8266/Arduino CI successfull!");
  } else {
    Serial.println("esp8266/Arduino CI has failed");
  }
  Serial.println("reply was:");
  Serial.println("==========");
  Serial.println(line);
  Serial.println("==========");
  Serial.println("closing connection");
}

void loop() {
}

The serial monitor output is:

WiFi connected
IP address: 
192.168.160.10
connecting to api.nasa.gov
connection failed

It is possible that you can guide me regarding this problem?

@shannonzchanz
Copy link

Hello, I am experiencing the same problem as well. I attempted to change the return statement to true in my appdata, hardware folder.

`
class TLSTraits : public TransportTraits
{
public:
TLSTraits(const String& fingerprint) :
_fingerprint(fingerprint)
{
}

std::unique_ptr<WiFiClient> create() override
{
    return std::unique_ptr<WiFiClient>(new WiFiClientSecure());

}

bool verify(WiFiClient& client, const char* host) override
{
    auto wcs = reinterpret_cast<WiFiClientSecure&>(client);
    //return wcs.verify(_fingerprint.c_str(), host);          //original code
    return true;
}`

@iamalonso
Copy link

Hello @shannonzchanz

  1. What kind of request are you doing: GET or POST?
  2. What website are you sending these request to?
  3. Could you attach your Arduino code?

@liebman
Copy link
Contributor

liebman commented Dec 13, 2017

@shannonzchanz try this PR #3933

@shannonzchanz
Copy link

I have resolved it! It's just that my esp8266 doesn't reach the localhost. So, I would have to publish it online. Thanks for the help.

@jLynx
Copy link

jLynx commented Apr 18, 2019

I am still having the same issue with not being able to load an https website.

This is what I have currently tried

http.begin("https://myFAKEsite.org/api/test", "6AEE739DA4EE7A518C87E2E96691F46136C9F060");      
    http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    
    int httpCode = http.POST(postData);   //Send the request
    String payload = http.getString();                  //Get the response payload

But I still get -1, even with passing the current SH1 key for that specific site.

What am I doing wrong here?

@hutch120
Copy link

hutch120 commented Sep 7, 2019

Hi @theyogeshrathod Looks like maybe you are fairly new to Github, and just starting out with ESP boards? Please be aware that github issue lists are generally reserved for bugs and I'd suggest that a better place to ask random questions like this is on the StackOverflow website.

Also, if you do post a response to a thread please ensure you read and understand the issue first, or you just annoy people. This thread clearly has nothing to do with your problem, it is related to an issue with HTTPS as it says in the issue title.

Also, I suggest you check if a thread has been updated recently, this hasn't had an actual answer since 2017 and probably should be locked and closed.

@igrr please consider locking this issue.

@theyogeshrathod
Copy link

Oh, sorry @hutch120. I will just delete the above comment. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests