Forum

API & Encryption
 
Notifications
Retirer tout

API & Encryption

12 Posts
4 Utilisateurs
0 Reactions
5,815 Vu
Fanch
(@fsouliers)
Posts: 86
Estimable Member
Début du sujet
 
[#7154]

Hello all and happy new year !

I had troubles with the API system, particularly regarding the encryption system. It follows this previous locked discussion :  https://www.projeqtor.org/fr/forum-fr/5-ask-questions/10767-python-connection-to-api

I have the same problem as Xav38 : the API works perfectly if I bypass the decryption system in the "if" block line 269 of api/index.php, replacing the

$data=AesCtr::decrypt ...

with a dirty

$data=$dataEncoded ;

Just for information, here is the python script :

import pycurl

from io import BytesIO

import ast

from Crypto.Cipher

import AES

import binascii

from base64 import b64encode, b64decode

import json 

login="admin.api"

key="d87b5ca7c5fdf827aa35914e966bf768"  

##################### Reading test###################

buffer = BytesIO()

c_getProj = pycurl.Curl()

c_getProj.setopt(c_getProj.URL, 'http://localhost/projeqtor/api/Project/all')

c_getProj.setopt(c_getProj.HTTPAUTH , pycurl.HTTPAUTH_BASIC)

c_getProj.setopt(c_getProj.USERPWD , "%s:%s"%(login,key))

#c_getProj.setopt(c_getProj.SSL_VERIFYPEER, False)

c_getProj.setopt(pycurl.WRITEDATA, buffer) 

c_getProj.perform()

c_getProj.close() 

body = ast.literal_eval(buffer.getvalue().decode("UTF-8")) 

# Juste pour simuler une sélection

projetRetenu = [x for x in body["items"] if x["nameStatus"] == 'qualified'][0]

print (projetRetenu)

 

##################### Writing test###################

# Définition d’une activité à créer

activite = {"name" : "Activité de test","idActivityType" : "26","idProject" : projetRetenu["id"],"idStatus" : "1","idResource" : "3","creationDate" : "2020-12-24",'idActivityPlanningMode': '1'} 

c_setAct = pycurl.Curl()

c_setAct.setopt(c_setAct.URL, 'http://localhost/projeqtor/api/Activity')

c_setAct.setopt(c_setAct.HTTPAUTH , pycurl.HTTPAUTH_BASIC)

c_setAct.setopt(c_setAct.USERPWD , "%s:%s"%(login,key)) 

res = json.dumps(activite)

c_setAct.setopt(c_setAct.CUSTOMREQUEST, "POST")

c_setAct.setopt(c_setAct.POSTFIELDS, res)

c_setAct.perform()

c_setAct.close()

In that case, I can see the succesful result in the logs :

===== TRACE ===== {"apiResult":"OK", "apiResultMessage":"Activité #432 inséré", "id":"432", "reference":"-TAS-40", "name":"Activité de test", "idActivityType":"26", ...

Now, regarding the encryption, I use pycryptodome and I use it with several other encrypted APIs not written in python ... and it works, so I consider this library as reliable (at least in a first approach).
Here is the writing test part of the script to test encryption / decryption (replacing previous one) :

##################### Writing test###################

# Définition d’une activité à créer

activite = {"name" : "Activité de test","idActivityType" : "26","idProject" : projetRetenu["id"],"idStatus" : "1","idResource" : "3","creationDate" : "2020-12-24",'idActivityPlanningMode': '1'}  

c_setAct = pycurl.Curl()

c_setAct.setopt(c_setAct.URL, 'http://localhost/projeqtor/api/Activity')

c_setAct.setopt(c_setAct.HTTPAUTH , pycurl.HTTPAUTH_BASIC)

c_setAct.setopt(c_setAct.USERPWD , "%s:%s"%(login,key)) 

print("-----------------------------------")

res = json.dumps(activite)

print("Chaine json : " + res) 

key_b=binascii.a2b_hex(key)

cipher = AES.new(key_b, AES.MODE_CTR, nonce=binascii.a2b_hex("CAFEBEEFCAFEBEEF")) # Forced 8 bytes nonce as this is the expected size on server side

ct_bytes = cipher.encrypt(res.encode("utf-8"))

ciphertext = b64encode(cipher.nonce + ct_bytes)

print("Texte encrypté : " + str(ciphertext))

print("Texte b64decode :" + str(b64decode(ciphertext))) 

c_setAct.setopt(c_setAct.CUSTOMREQUEST, "POST")

c_setAct.setopt(c_setAct.POSTFIELDS, ciphertext)

c_setAct.perform()

c_setAct.close() 


# just to test local decryption

tmp = b64decode(ciphertext)

cipher_d = AES.new(key_b, AES.MODE_CTR, nonce=tmp[:8])

msg_d = cipher_d.decrypt(tmp[8:])

print("Texte décrypté : " + str(msg_d))

The output is (I added return lines) :

-----------------------------------
Chaine json : {"name": "Activitu00e9 de test", "idActivityType": "26", "idProject": "2", "idStatus": "1", "idResource": "3", "creationDate": "2020-12-24", "idActivityPlanningMode": "1"}


Texte encrypté : b'yv6+78r+vu/Z4Ln+J0/Ke0pUDZrbrQHKtql/wxi3ZESJGDQk/fm09NN270CSpUmIsQ/cwjUT1U+5FdwSBcCreT/uNyVS13N1WqmWMtg1UjNsfjrgiD7DF5A+K6iqv9CMUeHQOheczLbtz+XQjC/ZUXCc+uVOagwrX1R2WoPjrqd1yERIOEFr8v/omdf1LIeSVeocqPP2JexfSZ957uK9tCaECluEBekBizAGU4R80Q2evlG9'


Texte b64decode :b"xcaxfexbexefxcaxfexbexefxd9xe0xb9xfe'Oxca{JTrx9axdbxadx01xcaxb6xa9x7fxc3x18xb7dDx89x184$xfdxf9xb4xf4xd3vxef@x92xa5Ix88xb1x0fxdcxc25x13xd5Oxb9x15xdcx12x05xc0xaby?xee7%Rxd7suZxa9x962xd85R3l~:xe0x88>xc3x17x90>+xa8xaaxbfxd0x8cQxe1xd0:x17x9cxccxb6xedxcfxe5xd0x8c/xd9Qpx9cxfaxe5Njx0c+_TvZx83xe3xaexa7uxc8DH8Akxf2xffxe8x99xd7xf5,x87x92Uxeax1cxa8xf3xf6%xec_Ix9fyxeexe2xbdxb4&x84n[x84x05xe9x01x8b0x06Sx84|xd1rx9exbeQxbd"


Texte décrypté : b'{"name": "Activit\u00e9 de test", "idActivityType": "26", "idProject": "2", "idStatus": "1", "idResource": "3", "creationDate": "2020-12-24", "idActivityPlanningMode": "1"}'

On the server side, the b64 decoded string is exactly the same, the nonce is properly extracted ($ctrTxt) ... but the string is not properly decrypted 🙁

I had a look to external/phpAES/aesctr.class.php ... and several points let me think there might be some implementation issue : 

At the end, solving this problem seems quite time consumming and maybe a workaround could be acceptable : would it be possible to have a setting to activate / deactivate encryption for the API ?
By the way, why is the GET unencrypted ?

Any help is welcome ... :/


 
Posté : 04/01/2021 6:47 pm
Fanch
(@fsouliers)
Posts: 86
Estimable Member
Début du sujet
 

Btw, maybe https://github.com/stymiee/php-simple-encryption could help.


 
Posté : 04/01/2021 7:07 pm
(@babynus)
Posts: 14952
Membre Admin
 

data must be encrypted with AES using the API key, and default uses AES decryption with 128bits keys .
If your encryption uses other key length (256 or else), you can define $aesKeyLength in parameters.php 


 
Posté : 04/01/2021 7:13 pm
Fanch
(@fsouliers)
Posts: 86
Estimable Member
Début du sujet
 

Hello,
I added the script to have a clearer comprehension.
The key is a 128bits, as generated for the dedicated user I created in Projeqtor (see lines 15 & 61).


 
Posté : 05/01/2021 9:44 am
(@babynus)
Posts: 14952
Membre Admin
 

Hi
I don't know python, so it's a bit hard to read for me.
But I don't understand why you encode in base 64 the encrypted data.

Here is some PHP code that works (maybe AES encrption should be changed to 128)

$fullUrl="http://myserver/api/Ticket";
$data='{"id":"1", "name":"name to be changed for Ticket 1"}';
$data=AesCtr::encrypt($data, 'ApiKeyForUserProjeqtor', 256);
$curl = curl_init($fullUrl);
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($curl, CURLOPT_USERPWD, "projeqtor:projeqtor");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
$curl_response = curl_exec($curl);
echo $curl_response;
curl_close($curl);


 
Posté : 06/01/2021 11:03 am
Fanch
(@fsouliers)
Posts: 86
Estimable Member
Début du sujet
 

Hello @babynus,
following this topic I have had an intern who developped an apiV2 using libsodium so we can have a full encryption of the data. We currently have written the php client classes and the python client classes (I am struggeling to find some time to write a C++/Qt and C# client). We fully tested it on v9.2.2 (and merged evolutions of API) and I am going to validate the upgrade in v9.2.3.
It works really well (even if some improvements will be welcome), so I was wondering if you could be interested in such a contribution? If yes, I obviously provide you all the required code, explanations and eventually some modification to better fit into the original projeqtor source code. 


 
Posté : 14/09/2021 7:02 pm
(@babynus)
Posts: 14952
Membre Admin
 

Yes, please share your code.
We'll have a look and may include it in community version.


 
Posté : 14/09/2021 8:02 pm
(@stephan123)
Posts: 4
Active Member
 

Dear @babynus,
I'd appreciate if you could consider the encryption functionality. I more than once had to deal with project clients/users complaining about the existing encryption.
Most appreciated thanks,
StephanE


 
Posté : 16/09/2021 11:33 am
Fanch
(@fsouliers)
Posts: 86
Estimable Member
Début du sujet
 

Hello,

In the zip, you can find enclosed : 
- apiv2 folder : server side php code. sodium has to be enabled on the server (uncomment extension in php.ini)
- php client folder : client side class. Actually, as a user of the API you only uses the class ProjeqtorRequests (which itself uses DataEncryption)
- python client folder : exact same thing but in python and with an example of use (be careful, last calls delete the Activity 2891 if it exists ...)

Some improvement have to be made to be integrated properly, but it is functionnal and is a good basis of discussion for an integration process. I’d be glad to talk with you about it if you are interested.

Have a nice day,


 
Posté : 17/09/2021 9:45 am
(@babynus)
Posts: 14952
Membre Admin
 

Hi,

Thanks for sharing your code.
Ticket #5699 will deal with that point.
 


 
Posté : 17/09/2021 11:43 am
(@bgenolini)
Posts: 45
Trusted Member
 

Hello,
Could it be more efficient to disable the encryption for sites using the https protocol? In this case, this protocol ensures a valid encryption. So far, the immediate solution we could implement is the "dirty" manipulation proposed by fsouliers (thanks!). But could it be a rapid fix to discard it, even by installation options?

In our community, Python development is a real asset, since this language is adopted by many persons. So I think it is worth achieving this feature.


Bernard

 
Posté : 06/04/2022 11:21 am
(@babynus)
Posts: 14952
Membre Admin
 

No, not as is 
Because encryption with the API Key ensures that the source of data is correcly identified.
Otherwise a hacker could easily post some request to your ProjeQtOr instance and destoy your data.


 
Posté : 06/04/2022 1:07 pm
Share:
Retour en haut